/*
* eID Applet Project.
* Copyright (C) 2008-2012 FedICT.
* Copyright (C) 2014 e-Contract.be BVBA.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/
package be.fedict.eid.applet.service;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import be.fedict.eid.applet.service.impl.CleanSessionProtocolStateListener;
import be.fedict.eid.applet.service.impl.HttpServletProtocolContext;
import be.fedict.eid.applet.service.impl.HttpServletRequestHttpReceiver;
import be.fedict.eid.applet.service.impl.HttpServletResponseHttpTransmitter;
import be.fedict.eid.applet.service.impl.RequestContext;
import be.fedict.eid.applet.service.impl.handler.MessageHandler;
import be.fedict.eid.applet.shared.AppletProtocolMessageCatalog;
import be.fedict.eid.applet.shared.annotation.ResponsesAllowed;
import be.fedict.eid.applet.shared.protocol.ProtocolStateMachine;
import be.fedict.eid.applet.shared.protocol.Transport;
import be.fedict.eid.applet.shared.protocol.Unmarshaller;
/**
* The eID applet service abstract Servlet. This abstract servlet is the basis
* for the classic Java EE 5 AppletServiceServlet, and the Java EE 6 CDI based
* version.
*
* @author Frank Cornelis
*/
public abstract class AbstractAppletServiceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Log LOG = LogFactory.getLog(AbstractAppletServiceServlet.class);
private Unmarshaller unmarshaller;
private static final String SKIP_SECURE_CONNECTION_CHECK_INIT_PARAM = "SkipSecureConnectionCheck";
private boolean skipSecureConnectionCheck;
public AbstractAppletServiceServlet() {
super();
LOG.debug("constructor");
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
LOG.debug("init");
this.unmarshaller = new Unmarshaller(new AppletProtocolMessageCatalog());
String skipSecureConnectionCheck = config.getInitParameter(SKIP_SECURE_CONNECTION_CHECK_INIT_PARAM);
if (null != skipSecureConnectionCheck) {
this.skipSecureConnectionCheck = Boolean.parseBoolean(skipSecureConnectionCheck);
LOG.debug("skipping secure connection check: " + this.skipSecureConnectionCheck);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
LOG.debug("doGet");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>eID Applet Service</title></head>");
out.println("<body>");
out.println("<h1>eID Applet Service</h1>");
out.println("<p>The eID Applet Service should not be accessed directly.</p>");
out.println("</body></html>");
out.close();
}
/**
* This method needs to be implemented by servlets that extend this abstract
* base servlet.
*
* @param messageClass
* @return
*/
protected abstract <T> MessageHandler<T> getMessageHandler(Class<T> messageClass);
@SuppressWarnings("unchecked")
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
LOG.debug("doPost");
/*
* First retrieve the HTTP headers. The unmarshaller may digest the
* body, which makes it impossible to retrieve the headers afterwards.
*/
Map<String, String> httpHeaders = new HashMap<String, String>();
Enumeration<String> headerNamesEnum = request.getHeaderNames();
while (headerNamesEnum.hasMoreElements()) {
String headerName = headerNamesEnum.nextElement();
httpHeaders.put(headerName, request.getHeader(headerName));
}
/*
* Incoming message unmarshaller.
*/
HttpServletRequestHttpReceiver httpReceiver = new HttpServletRequestHttpReceiver(request,
this.skipSecureConnectionCheck);
Object transferObject;
try {
transferObject = this.unmarshaller.receive(httpReceiver);
} catch (Exception e) {
LOG.debug("unmarshaller error: " + e.getMessage(), e);
throw new RuntimeException("unmarshaller error: " + e.getMessage(), e);
}
/*
* Protocol state checker for incoming message.
*/
HttpServletProtocolContext protocolContext = new HttpServletProtocolContext(request);
ProtocolStateMachine protocolStateMachine = new ProtocolStateMachine(protocolContext);
CleanSessionProtocolStateListener cleanSessionProtocolStateListener = new CleanSessionProtocolStateListener(
request);
protocolStateMachine.addProtocolStateListener(cleanSessionProtocolStateListener);
RequestContext requestContext = new RequestContext(request);
protocolStateMachine.addProtocolStateListener(requestContext);
protocolStateMachine.checkRequestMessage(transferObject);
/*
* Message dispatcher
*/
Class<?> messageClass = transferObject.getClass();
MessageHandler messageHandler = getMessageHandler(messageClass);
if (null == messageHandler) {
throw new ServletException("unsupported message");
}
HttpSession session = request.getSession();
Object responseMessage = messageHandler.handleMessage(transferObject, httpHeaders, request, session);
/*
* Check outgoing messages for protocol constraints.
*/
ResponsesAllowed responsesAllowedAnnotation = messageClass.getAnnotation(ResponsesAllowed.class);
if (null != responsesAllowedAnnotation) {
/*
* Make sure the message handlers respect the protocol.
*/
if (null == responseMessage) {
throw new ServletException("null response message while @ResponsesAllowed constraint was set");
}
Class<?>[] responsesAllowed = responsesAllowedAnnotation.value();
if (false == isOfClass(responseMessage, responsesAllowed)) {
throw new ServletException("response message type incorrect");
}
}
/*
* Protocol state checker for outgoing message.
*/
protocolStateMachine.checkResponseMessage(responseMessage);
/*
* Marshall outgoing message.
*/
if (null != responseMessage) {
HttpServletResponseHttpTransmitter httpTransmitter = new HttpServletResponseHttpTransmitter(response);
Transport.transfer(responseMessage, httpTransmitter);
}
}
private boolean isOfClass(Object object, Class<?>[] classes) {
for (Class<?> clazz : classes) {
if (clazz.equals(object.getClass())) {
return true;
}
}
return false;
}
}