/* * Copyright (c) 2006 jDiameter. * https://jdiameter.dev.java.net/ * * License: Lesser General Public License (LGPL) * * e-mail: erick.svenson@yahoo.com * */ package org.jdiameter.server.impl.app.auth; import static org.jdiameter.common.api.app.auth.ServerAuthSessionState.DISCONNECTED; import static org.jdiameter.common.api.app.auth.ServerAuthSessionState.IDLE; import static org.jdiameter.common.api.app.auth.ServerAuthSessionState.OPEN; import static org.jdiameter.server.impl.app.auth.Event.Type.RECEIVE_AUTH_REQUEST; import static org.jdiameter.server.impl.app.auth.Event.Type.RECEVE_ASR_ANSWER; import static org.jdiameter.server.impl.app.auth.Event.Type.TIMEOUT_EXPIRES; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jdiameter.api.Answer; import org.jdiameter.api.EventListener; import org.jdiameter.api.IllegalDiameterStateException; import org.jdiameter.api.InternalException; import org.jdiameter.api.NetworkReqListener; import org.jdiameter.api.OverloadException; import org.jdiameter.api.Request; import org.jdiameter.api.RouteException; import org.jdiameter.api.Session; import org.jdiameter.api.app.AppAnswerEvent; import org.jdiameter.api.app.AppEvent; import org.jdiameter.api.app.AppRequestEvent; import org.jdiameter.api.app.StateChangeListener; import org.jdiameter.api.app.StateEvent; import org.jdiameter.api.auth.ServerAuthSession; import org.jdiameter.api.auth.ServerAuthSessionListener; import org.jdiameter.api.auth.events.AbortSessionAnswer; import org.jdiameter.api.auth.events.AbortSessionRequest; import org.jdiameter.api.auth.events.ReAuthRequest; import org.jdiameter.api.auth.events.SessionTermAnswer; import org.jdiameter.api.auth.events.SessionTermRequest; import org.jdiameter.client.impl.app.auth.ClientAuthSessionImpl; import org.jdiameter.common.api.app.IAppSessionState; import org.jdiameter.common.api.app.auth.ClientAuthSessionState; import org.jdiameter.common.api.app.auth.IAuthMessageFactory; import org.jdiameter.common.api.app.auth.IServerAuthActionContext; import org.jdiameter.common.api.app.auth.ServerAuthSessionState; import org.jdiameter.common.impl.app.AppAnswerEventImpl; import org.jdiameter.common.impl.app.auth.AbortSessionAnswerImpl; import org.jdiameter.common.impl.app.auth.AbortSessionRequestImpl; import org.jdiameter.common.impl.app.auth.AppAuthSessionImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServerAuthSessionImpl extends AppAuthSessionImpl implements ServerAuthSession, EventListener<Request, Answer>, NetworkReqListener { private static final long serialVersionUID = 1L; protected static final Logger logger = LoggerFactory.getLogger(ClientAuthSessionImpl.class); protected ServerAuthSessionState state = IDLE; protected boolean stateless; protected long tsTimeout; protected ScheduledFuture tsTask; protected IAuthMessageFactory factory; protected IServerAuthActionContext context; protected ServerAuthSessionListener listener; private Lock sendAndStateLock = new ReentrantLock(); // =================== CONSTRUCTORS public ServerAuthSessionImpl(Session session, Request initialRequest, ServerAuthSessionListener lst, IAuthMessageFactory fct, long tsTimeout, boolean stateless, StateChangeListener... scListeners) { if (session == null) { throw new IllegalArgumentException("Session can not be null"); } if (lst == null) { throw new IllegalArgumentException("Session listener can not be null"); } this.session = session; appId = fct.getApplicationId(); listener = lst; factory = fct; this.tsTimeout = tsTimeout; this.stateless = stateless; this.session.setRequestListener(this); for (StateChangeListener l : scListeners) { addStateChangeNotification(l); } if (listener instanceof IServerAuthActionContext) { context = (IServerAuthActionContext) listener; } for (StateChangeListener l : scListeners) { addStateChangeNotification(l); } //processRequest(initialRequest); } public void sendAuthAnswer(AppAnswerEvent appAnswerEvent) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException { send(null, appAnswerEvent); } public void sendReAuthRequest(ReAuthRequest reAuthRequest) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException { send(null, reAuthRequest); } public void sendAbortSessionRequest(AbortSessionRequest abortSessionRequest) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException { send(Event.Type.SEND_ASR_REQUEST, abortSessionRequest); } public void sendSessionTerminationAnswer(SessionTermAnswer sessionTermAnswer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException { send(null, sessionTermAnswer); } protected void send(Event.Type type, AppEvent event) throws InternalException { try { sendAndStateLock.lock(); if (type != null) { handleEvent(new Event(type, event)); } session.send(event.getMessage(), this); } catch (Exception e) { throw new InternalException(e); } finally { sendAndStateLock.unlock(); } } public boolean isStateless() { return stateless; } protected void setState(ServerAuthSessionState newState) { IAppSessionState oldState = state; state = newState; for (StateChangeListener i : stateListeners) { i.stateChanged((Enum) oldState, (Enum) newState); } } public <E> E getState(Class<E> eClass) { return eClass == ClientAuthSessionState.class ? (E) state : null; } public boolean handleEvent(StateEvent event) throws InternalException, OverloadException { return stateless ? handleEventForStatelessSession(event) : handleEventForStatefullSession(event); } public boolean handleEventForStatelessSession(StateEvent event) throws InternalException, OverloadException { try { switch (state) { case IDLE: switch ((Event.Type) event.getType()) { case RECEIVE_AUTH_REQUEST: listener.doAuthRequestEvent(this, (AppRequestEvent) event.getData()); setState(IDLE); break; default: logger.debug("Unknown event {}", event.getType()); break; } break; } } catch (Throwable t) { throw new InternalException(t); } return true; } public boolean handleEventForStatefullSession(StateEvent event) throws InternalException, OverloadException { ServerAuthSessionState oldState = state; try { switch (state) { case IDLE: { switch ((Event.Type) event.getType()) { case RECEIVE_AUTH_REQUEST: try { listener.doAuthRequestEvent(this, (AppRequestEvent) event.getData()); setState(OPEN); } catch (Exception e) { setState(IDLE); } break; case RECEIVE_STR_REQUEST: try { listener.doSessionTerminationRequestEvent(this, (SessionTermRequest) event.getData()); } catch (Exception e) { logger.debug("Can not handle event", e); } break; case SEND_ASR_REQUEST: setState(DISCONNECTED); break; case TIMEOUT_EXPIRES: if (context != null) { context.accessTimeoutElapses(this); } setState(IDLE); break; default: logger.debug("Unknown event {}", event.getType()); break; } break; } case OPEN: { switch ((Event.Type) event.getType()) { case RECEIVE_AUTH_REQUEST: try { listener.doAuthRequestEvent(this, (AppRequestEvent) event.getData()); } catch (Exception e) { setState(IDLE); } break; case RECEIVE_STR_REQUEST: try { listener.doSessionTerminationRequestEvent(this, (SessionTermRequest) event.getData()); } catch (Exception e) { logger.debug("Can not handle event", e); } setState(IDLE); break; case SEND_ASR_REQUEST: setState(DISCONNECTED); break; default: logger.debug("Unknown event {}", event.getType()); break; } break; } case DISCONNECTED: { switch ((Event.Type) event.getType()) { case SEND_ASR_FAILURE: setState(DISCONNECTED); break; case RECEVE_ASR_ANSWER: listener.doAbortSessionAnswerEvent(this, (AbortSessionAnswer) event.getData()); setState(IDLE); break; default: logger.debug("Unknown event {}", event.getType()); break; } break; } default: { logger.debug("Unknown state {}", state); break; } } // post processing if (oldState != state) { if (OPEN.equals(state) && context != null) { scheduler.schedule(new Runnable() { public void run() { if (context != null) { try { handleEvent(new Event(TIMEOUT_EXPIRES, null)); } catch (Exception e) { logger.debug("Can not handle event", e); } } } }, context.createAccessTimer(), TimeUnit.MILLISECONDS); } } } catch (Throwable t) { throw new InternalException(t); } return true; } public void receivedSuccessMessage(Request request, Answer answer) { try { sendAndStateLock.lock(); if (request.getCommandCode() == factory.getAuthMessageCommandCode()) { handleEvent(new Event(RECEIVE_AUTH_REQUEST, factory.createAuthRequest(request))); } else if (request.getCommandCode() == AbortSessionRequestImpl.code) { handleEvent(new Event(RECEVE_ASR_ANSWER, new AbortSessionAnswerImpl(answer))); } else { listener.doOtherEvent(this, factory.createAuthRequest(request), new AppAnswerEventImpl(answer)); } } catch (Exception e) { logger.debug("Can not handle event", e); } finally { sendAndStateLock.unlock(); } } public void timeoutExpired(Request request) { try { if (request.getCommandCode() == AbortSessionRequestImpl.code) { handleEvent(new Event(Event.Type.SEND_ASR_FAILURE, new AbortSessionRequestImpl(request))); } else { logger.debug("Timeout for unknown request {}", request); } } catch (Exception e) { logger.debug("Can not handle event", e); } } public Answer processRequest(Request request) { if (request != null) { if (request.getCommandCode() == factory.getAuthMessageCommandCode()) { try { sendAndStateLock.lock(); handleEvent(new Event(RECEIVE_AUTH_REQUEST, factory.createAuthRequest(request))); } catch (Exception e) { logger.debug("Can not handle event", e); } finally { sendAndStateLock.unlock(); } } else { try { listener.doOtherEvent(this, factory.createAuthRequest(request), null); } catch (Exception e) { logger.debug("Can not handle event", e); } } } return null; } }