/****************************************************************************
* Copyright (C) 2013 HS Coburg.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.control.binding.http.handler;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.UnsupportedCharsetException;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.openecard.addon.AddonManager;
import org.openecard.addon.AddonNotFoundException;
import org.openecard.addon.AddonSelector;
import org.openecard.addon.bind.AppPluginAction;
import org.openecard.addon.bind.AuxDataKeys;
import org.openecard.addon.bind.BindingResult;
import org.openecard.addon.bind.BindingResultCode;
import org.openecard.addon.bind.Body;
import org.openecard.apache.http.HttpEntity;
import org.openecard.apache.http.HttpEntityEnclosingRequest;
import org.openecard.apache.http.HttpException;
import org.openecard.apache.http.HttpRequest;
import org.openecard.apache.http.HttpResponse;
import org.openecard.apache.http.HttpStatus;
import org.openecard.apache.http.ParseException;
import org.openecard.apache.http.entity.BasicHttpEntity;
import org.openecard.apache.http.entity.ContentType;
import org.openecard.apache.http.entity.StringEntity;
import org.openecard.apache.http.protocol.HttpContext;
import org.openecard.common.util.FileUtils;
import org.openecard.common.util.HttpRequestLineUtils;
import org.openecard.control.binding.http.common.DocumentRoot;
import org.openecard.control.binding.http.common.HeaderTypes;
import org.openecard.control.binding.http.common.Http11Response;
import org.openecard.control.binding.http.handler.common.DefaultHandler;
import org.openecard.control.binding.http.handler.common.FileHandler;
import org.openecard.control.binding.http.handler.common.IndexHandler;
import org.openecard.ws.marshal.WSMarshaller;
import org.openecard.ws.marshal.WSMarshallerException;
import org.openecard.ws.marshal.WSMarshallerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
*
* @author Dirk Petrautzki <petrautzki@hs-coburg.de>
*/
public class HttpAppPluginActionHandler extends HttpControlHandler {
private static final Logger logger = LoggerFactory.getLogger(HttpAppPluginActionHandler.class);
private final AddonManager addonManager;
private final AddonSelector selector;
private final WSMarshaller marshaller;
public HttpAppPluginActionHandler(AddonManager addonManager) {
super("*");
this.addonManager = addonManager;
this.selector = new AddonSelector(addonManager);
try {
marshaller = WSMarshallerFactory.createInstance();
} catch (WSMarshallerException e) {
throw new RuntimeException(e);
}
}
@Override
public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext context) throws HttpException,
IOException {
logger.debug("HTTP request: {}", httpRequest.toString());
// deconstruct request uri
String uri = httpRequest.getRequestLine().getUri();
URI requestURI = URI.create(uri);
String path = requestURI.getPath();
String resourceName = path.substring(1, path.length()); // remove leading '/'
// find suitable addon
try {
AppPluginAction action = selector.getAppPluginAction(resourceName);
HttpResponse response;
if (addonManager == null) {
response = new Http11Response(HttpStatus.SC_INTERNAL_SERVER_ERROR);
StringEntity entity = new StringEntity("Addon initialization failed.", "UTF-8");
response.setEntity(entity);
} else {
String rawQuery = requestURI.getRawQuery();
Map<String, String> queries = new HashMap<String, String>(0);
if (rawQuery != null) {
queries = HttpRequestLineUtils.transform(rawQuery);
}
Body body = null;
if (httpRequest instanceof HttpEntityEnclosingRequest) {
logger.debug("Request contains an entity.");
body = getRequestBody(httpRequest);
}
BindingResult bindingResult = action.execute(body, queries, null);
response = createHTTPResponseFromBindingResult(bindingResult);
}
response.setParams(httpRequest.getParams());
logger.debug("HTTP response: {}", response);
Http11Response.copyHttpResponse(response, httpResponse);
} catch (AddonNotFoundException ex) {
if (path.equals("/")) {
new IndexHandler().handle(httpRequest, httpResponse, context);
} else if (path.startsWith("/")) {
new FileHandler(new DocumentRoot("/www", "/www-files")).handle(httpRequest, httpResponse, context);
} else {
new DefaultHandler().handle(httpRequest, httpResponse, context);
}
}
}
private HttpEntity createHTTPEntityFromBody(BindingResult bindingResult) {
Body responseBody = bindingResult.getBody();
if (responseBody != null) {
logger.debug("BindingResult contains a body.");
BasicHttpEntity entity = new BasicHttpEntity();
try {
Node value = responseBody.getValue();
if (value.getFirstChild().getNodeName().equals("base64Content")) {
byte[] bytes = value.getFirstChild().getFirstChild().getNodeValue().getBytes("UTF-8");
entity.setContent(new ByteArrayInputStream(bytes));
} else {
entity.setContent(new ByteArrayInputStream(marshaller.doc2str(value).getBytes("UTF-8")));
}
} catch (TransformerException e) {
logger.error("Failed to set http entity", e);
} catch (UnsupportedEncodingException e) {
logger.error("Failed to set http entity", e);
}
entity.setContentType(ContentType.create(responseBody.getMimeType()).toString());
return entity;
}
logger.debug("BindingResult contains NO body.");
return null;
}
private HttpEntity createHTTPEntity(BindingResult bindingResult) {
HttpEntity entity = createHTTPEntityFromBody(bindingResult);
if (entity == null && bindingResult.getResultMessage() != null) {
try {
entity = new StringEntity(bindingResult.getResultMessage());
} catch (UnsupportedEncodingException e) {
logger.error("StringEntity creation failed. Returned entity will be null", e);
}
}
return entity;
}
private HttpResponse createHTTPResponseFromBindingResult(BindingResult bindingResult) {
BindingResultCode resultCode = bindingResult.getResultCode();
logger.debug("Recieved BindingResult with ResultCode {}", resultCode);
HttpResponse response;
if (resultCode.equals(BindingResultCode.OK)) {
response = new Http11Response(HttpStatus.SC_OK);
} else if (resultCode.equals(BindingResultCode.REDIRECT)) {
response = new Http11Response(HttpStatus.SC_SEE_OTHER);
String location = bindingResult.getAuxResultData().get(AuxDataKeys.REDIRECT_LOCATION);
if (location != null && ! location.isEmpty()) {
response.addHeader(HeaderTypes.LOCATION.fieldName(), location);
} else {
// redirect requires a location field
logger.error("No redirect address available in given BindingResult instance.");
response = new Http11Response(HttpStatus.SC_INTERNAL_SERVER_ERROR);
}
} else if (resultCode.equals(BindingResultCode.WRONG_PARAMETER)) {
response = new Http11Response(HttpStatus.SC_BAD_REQUEST);
} else if (resultCode.equals(BindingResultCode.INTERNAL_ERROR)) {
response = new Http11Response(HttpStatus.SC_INTERNAL_SERVER_ERROR);
} else {
logger.error("Untreated result code: " + resultCode);
response = new Http11Response(HttpStatus.SC_INTERNAL_SERVER_ERROR);
}
HttpEntity entity = createHTTPEntity(bindingResult);
response.setEntity(entity);
return response;
}
private Body getRequestBody(HttpRequest httpRequest) throws IOException {
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) httpRequest;
HttpEntity entity = entityRequest.getEntity();
InputStream is = entity.getContent();
try {
DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fac.newDocumentBuilder();
Document d = builder.newDocument();
Element elemBase64 = d.createElement("base64Content");
elemBase64.setTextContent(FileUtils.toString(is));
d.appendChild(elemBase64);
return new Body(d, ContentType.get(entity).getMimeType());
} catch (UnsupportedCharsetException e) {
logger.error("Failed to create request body.", e);
} catch (ParseException e) {
logger.error("Failed to create request body.", e);
} catch (ParserConfigurationException e) {
logger.error("Failed to create request body.", e);
}
return null;
}
}