/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.eas.server.mina.platypus; import com.eas.client.login.PlatypusPrincipal; import com.eas.client.threetier.Request; import com.eas.client.threetier.Response; import com.eas.client.threetier.platypus.RequestEnvelope; import com.eas.client.threetier.requests.AccessControlExceptionResponse; import com.eas.client.threetier.requests.ExceptionResponse; import com.eas.client.threetier.requests.JsonExceptionResponse; import com.eas.client.threetier.requests.SqlExceptionResponse; import com.eas.script.JsObjectException; import com.eas.script.Scripts; import com.eas.server.*; import com.eas.server.DatabaseAuthorizer; import com.eas.util.IdGenerator; import java.net.NetPermission; import java.security.AccessControlException; import java.sql.SQLException; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.ScriptException; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /** * * @author pk, mg, ab */ public class PlatypusRequestsHandler extends IoHandlerAdapter { public static final String BAD_PROTOCOL_MSG = "The message is not Platypus protocol request."; public static final String GOT_SIGNATURE_MSG = "Got signature from client."; public static final String SESSION_ID = "platypus-session-id"; private static final String GENERAL_EXCEPTION_MESSAGE = "Exception on request of type %d | %s. Message: %s"; private static final String ACCESS_CONTROL_EXCEPTION_MESSAGE = "AccessControlException on request of type %d | %s. Message: %s."; private static final String SQL_EXCEPTION_MESSAGE = "SQLException on request of type %d | %s. Message: %s. sqlState: %s, errorCode: %d"; private static final String JS_OBJECT_EXCEPTION_MESSAGE = "JsObjectException on request of type %d | %s. Message: %s."; public static final int IDLE_TIME_EVENT = 5 * 60; // 5 minutes public static final int SESSION_TIME_OUT = 60 * 60; // 1 hour protected int sessionIdleCheckInterval = IDLE_TIME_EVENT; protected int sessionIdleTime = SESSION_TIME_OUT; private final PlatypusServer server; public PlatypusRequestsHandler(PlatypusServer aServer) { super(); server = aServer; } public int getSessionIdleCheckInterval() { return sessionIdleCheckInterval; } public void setSessionIdleCheckInterval(int aValue) { sessionIdleCheckInterval = aValue; } public int getSessionIdleTime() { return sessionIdleTime; } public void setSessionIdleTime(int aValue) { sessionIdleTime = aValue; } @Override public void sessionCreated(IoSession session) throws Exception { super.sessionCreated(session); session.getConfig().setBothIdleTime(sessionIdleCheckInterval); } @Override public void sessionIdle(IoSession ioSession, IdleStatus status) throws Exception { super.sessionIdle(ioSession, status); if (ioSession.getBothIdleCount() * sessionIdleCheckInterval >= sessionIdleTime) { ioSession.close(false); String sessionId = (String) ioSession.getAttribute(SESSION_ID); if (sessionId != null) { // A bit hacky, due to sessions nature. ioSession is NOT an equivalent of sessionManager.session, // but we use idle events as source of information about idle sessions server.getSessionManager().remove(sessionId); } else { Logger.getLogger(PlatypusRequestsHandler.class.getName()).severe("An attempt to remove system (null) session occured."); } } } /** * * @param ioSession * @param msg * @throws Exception */ @Override public void messageReceived(IoSession ioSession, Object msg) throws Exception { if (msg instanceof RequestEnvelope) { RequestEnvelope requestEnv = (RequestEnvelope) msg; try { Consumer<Exception> onError = (Exception ex) -> { if (ex instanceof SQLException) { SQLException sex = (SQLException) ex; Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, String.format(SQL_EXCEPTION_MESSAGE, requestEnv.request.getType(), requestEnv.request.getClass().getSimpleName(), sex.getMessage(), sex.getSQLState(), sex.getErrorCode())); ioSession.write(new SqlExceptionResponse(sex)); } else if (ex instanceof AccessControlException) { AccessControlException aex = (AccessControlException) ex; Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, String.format(ACCESS_CONTROL_EXCEPTION_MESSAGE, requestEnv.request.getType(), requestEnv.request.getClass().getSimpleName(), aex.getMessage())); ioSession.write(new AccessControlExceptionResponse(aex)); } else { Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, String.format(GENERAL_EXCEPTION_MESSAGE, requestEnv.request.getType(), requestEnv.request.getClass().getSimpleName(), ex.toString())); ioSession.write(new ExceptionResponse(ex)); } }; Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.FINE, "Request {0}", requestEnv.request.toString()); final RequestHandler<Request, Response> handler = (RequestHandler<Request, Response>) RequestHandlerFactory.getHandler(server, requestEnv.request); if (handler != null) { if (requestEnv.ticket == null) { try { Session session = server.getSessionManager().create(IdGenerator.genStringId()); DatabaseAuthorizer.authorize(server, requestEnv.userName, requestEnv.password, session.getSpace(), (PlatypusPrincipal aPrincipal) -> { requestEnv.ticket = session.getId(); // It is safe to put SESSION_ID attribute here because of request-response protocol nature. // So, the following call to session.getSpace().process() and further handling of the request is safe. ioSession.setAttribute(SESSION_ID, session.getId()); session.setPrincipal(aPrincipal); Scripts.LocalContext context = new Scripts.LocalContext(session.getPrincipal(), session); session.getSpace().process(context, () -> { handler.handle(session, (Response aResponse) -> { ioSession.write(aResponse); }, (Exception ex) -> { if (ex instanceof JsObjectException) { JsObjectException jsoex = (JsObjectException) ex; Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, String.format(JS_OBJECT_EXCEPTION_MESSAGE, requestEnv.request.getType(), requestEnv.request.getClass().getSimpleName(), jsoex.getMessage())); ioSession.write(new JsonExceptionResponse(jsoex, session.getSpace().toJson(jsoex.getData()))); } else { onError.accept(ex); } }); }); }, (Exception ex)->{ onError.accept(ex); }); } catch (ScriptException ex) { Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, null, ex); } } else { Session session = server.getSessionManager().get(requestEnv.ticket); if (session != null) { ioSession.setAttribute(SESSION_ID, session.getId()); Scripts.LocalContext context = new Scripts.LocalContext(session.getPrincipal(), session); session.getSpace().process(context, () -> { handler.handle(session, (Response aResponse) -> { ioSession.write(aResponse); }, (Exception ex) -> { if (ex instanceof JsObjectException) { JsObjectException jsoex = (JsObjectException) ex; Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, String.format(JS_OBJECT_EXCEPTION_MESSAGE, requestEnv.request.getType(), requestEnv.request.getClass().getSimpleName(), jsoex.getMessage())); ioSession.write(new JsonExceptionResponse(jsoex, session.getSpace().toJson(jsoex.getData()))); } else { onError.accept(ex); } }); }); } else { throw new AccessControlException("Bad session ticket.", new NetPermission("*")); } } } else { throw new IllegalStateException("Unknown request type " + requestEnv.request.getType()); } } catch (Throwable ex) { ioSession.write(new ExceptionResponse(ex)); Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, ex.toString()); } } else { throw new IllegalArgumentException(BAD_PROTOCOL_MSG); } } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { Logger.getLogger(PlatypusRequestsHandler.class.getName()).log(Level.SEVERE, cause.toString()); } @Override public void messageSent(IoSession ioSession, Object message) throws Exception { } }