/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
*
* Licensed 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.switchyard.component.http;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jboss.logging.Logger;
import org.switchyard.common.type.Classes;
import org.switchyard.component.http.composer.HttpRequestBindingData;
import org.switchyard.component.http.composer.HttpRequestInfo;
import org.switchyard.component.http.composer.HttpResponseBindingData;
import org.switchyard.security.SecurityServices;
/**
* Hanldes HTTP requests to invoke a SwitchYard service.
*
* @author Magesh Kumar B <mageshbk@jboss.com> (C) 2012 Red Hat Inc.
*/
public class HttpGatewayServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(HttpGatewayServlet.class);
private static final Map<String,String> LOCALNAMEMAP = new ConcurrentHashMap<String,String>();
private transient InboundHandler _handler;
private transient ClassLoader _classLoader;
// request.getLocalName() has proven expensive, so cache it
private static final String getLocalName(HttpServletRequest request) {
String localAddr = request.getLocalAddr();
String localName = LOCALNAMEMAP.get(localAddr);
if (localName == null) {
localName = request.getLocalName();
LOCALNAMEMAP.put(localAddr, localName);
}
return localName;
}
/**
* Set the SwitchYard component handler for this instance.
* @param handler the SwicthYard component handler
*/
public void setHandler(InboundHandler handler) {
_handler = handler;
}
/**
* Set the ClassLoader to use as a TCCL on execution.
* @param loader ClassLoader
*/
public void setClassLoader(ClassLoader loader) {
_classLoader = loader;
}
/**
* {@inheritDoc}
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* {@inheritDoc}
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* {@inheritDoc}
*/
public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* {@inheritDoc}
*/
public void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* {@inheritDoc}
*/
public void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* {@inheritDoc}
*/
public void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* {@inheritDoc}
*/
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handle(request, response);
}
/**
* Handles a HTTP Servlet request and responds.
* @param request the HttpServletRequest object
* @param response the HttpServletResponse object
* @throws ServletException if any Servlet exception
* @throws IOException if the streams could not be written or read
*/
public void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpRequestBindingData httpRequest = new HttpRequestBindingData();
try {
httpRequest.setContentType(new ContentType(request.getContentType()));
httpRequest.setBodyFromStream(request.getInputStream());
for (Enumeration<String> headerNames = request.getHeaderNames(); headerNames.hasMoreElements();) {
String name = headerNames.nextElement();
for (Enumeration<String> values = request.getHeaders(name); values.hasMoreElements();) {
String value = values.nextElement();
httpRequest.addHeader(name, value);
}
}
httpRequest.setRequestInfo(getRequestInfo(request));
} catch (IOException e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
HttpLogger.ROOT_LOGGER.unexpectedExceptionWhileReadingRequest(e);
}
ClassLoader origCl = null;
try {
if (_classLoader != null) {
origCl = Classes.setTCCL(_classLoader);
}
HttpResponseBindingData httpResponse = _handler.invoke(httpRequest);
if (httpResponse != null) {
Iterator<Map.Entry<String, List<String>>> entries = httpResponse.getHeaders().entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, List<String>> entry = entries.next();
String name = entry.getKey();
List<String> values = entry.getValue();
for (String value : values) {
response.addHeader(name, value);
}
}
if (httpResponse.getBodyBytes() != null) {
response.setStatus(httpResponse.getStatus());
httpResponse.writeBodyToStream(response.getOutputStream());
} else {
if (httpResponse.getStatus() != null) {
response.setStatus(httpResponse.getStatus());
} else {
// Consider it as a One-Way MEP
response.setStatus(HttpServletResponse.SC_ACCEPTED);
response.setContentLength(0);
}
}
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
HttpLogger.ROOT_LOGGER.unexpectedExceptionWhileWritingResponse(e);
} finally {
if (origCl != null) {
Classes.setTCCL(origCl);
}
}
}
/**
* Method for get request information from a servlet request.
*
* @param request ServletRequest
* @return Request information parsed by servlet container from a servlet request
*/
@SuppressWarnings("unchecked")
public HttpRequestInfo getRequestInfo(HttpServletRequest request) {
HttpRequestInfo requestInfo = new HttpRequestInfo();
requestInfo.setAuthType(request.getAuthType());
requestInfo.setCharacterEncoding(request.getCharacterEncoding());
requestInfo.setContentType(request.getContentType());
requestInfo.setContextPath(request.getContextPath());
requestInfo.setLocalAddr(request.getLocalAddr());
requestInfo.setLocalName(getLocalName(request));
requestInfo.setMethod(request.getMethod());
requestInfo.setProtocol(request.getProtocol());
requestInfo.setQueryString(request.getQueryString());
requestInfo.setRemoteAddr(request.getRemoteAddr());
requestInfo.setRemoteHost(request.getRemoteHost());
requestInfo.setRemoteUser(request.getRemoteUser());
requestInfo.setContentLength(request.getContentLength());
requestInfo.setRequestSessionId(request.getRequestedSessionId());
requestInfo.setRequestURI(request.getRequestURI());
requestInfo.setScheme(request.getScheme());
requestInfo.setServerName(request.getServerName());
requestInfo.setRequestPath(request.getServletPath());
String pathInfo = request.getPathInfo();
requestInfo.setPathInfo(pathInfo);
if (pathInfo != null) {
List<String> pathInfoTokens = requestInfo.getPathInfoTokens();
pathInfoTokens.addAll(Arrays.asList(request.getPathInfo().split("/")));
// remove empty tokens...
Iterator<String> tokensIterator = pathInfoTokens.iterator();
while (tokensIterator.hasNext()) {
if (tokensIterator.next().trim().length() == 0) {
tokensIterator.remove();
}
}
}
// Http Query params...
Map paramMap = request.getParameterMap();
if (paramMap != null) {
requestInfo.getQueryParams().putAll(paramMap);
}
// Credentials...
requestInfo.getCredentials().addAll(SecurityServices.getServletRequestCredentialExtractor().extract(request));
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(requestInfo);
}
return requestInfo;
}
}