All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdiameter.server.impl.app.acc.ServerAccSessionImpl Maven / Gradle / Ivy

The newest version!
 /*
  * TeleStax, Open Source Cloud Communications
  * Copyright 2011-2016, TeleStax Inc. and individual contributors
  * by the @authors tag.
  *
  * This program is free software: you can redistribute it and/or modify
  * under the terms of the GNU Affero General Public License as
  * published by the Free Software Foundation; either version 3 of
  * the License, or (at your option) any later version.
  *
  * This program 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 Affero General Public License for more details.
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see 
  *
  * This file incorporates work covered by the following copyright and
  * permission notice:
  *
  *   JBoss, Home of Professional Open Source
  *   Copyright 2007-2011, Red Hat, Inc. and individual contributors
  *   by the @authors tag. See the copyright.txt in the distribution for a
  *   full listing of individual contributors.
  *
  *   This is free software; you can redistribute it and/or modify it
  *   under the terms of the GNU Lesser General Public License as
  *   published by the Free Software Foundation; either version 2.1 of
  *   the License, or (at your option) any later version.
  *
  *   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, write to the Free
  *   Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  *   02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */

package org.jdiameter.server.impl.app.acc;

import static org.jdiameter.common.api.app.acc.ServerAccSessionState.IDLE;
import static org.jdiameter.common.api.app.acc.ServerAccSessionState.OPEN;

import java.io.Serializable;

import org.jdiameter.api.Answer;
import org.jdiameter.api.Avp;
import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.AvpSet;
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.ResultCode;
import org.jdiameter.api.RouteException;
import org.jdiameter.api.acc.ServerAccSession;
import org.jdiameter.api.acc.ServerAccSessionListener;
import org.jdiameter.api.acc.events.AccountAnswer;
import org.jdiameter.api.acc.events.AccountRequest;
import org.jdiameter.api.app.AppSession;
import org.jdiameter.api.app.StateChangeListener;
import org.jdiameter.api.app.StateEvent;
import org.jdiameter.client.api.ISessionFactory;
import org.jdiameter.common.api.app.IAppSessionState;
import org.jdiameter.common.api.app.acc.IServerAccActionContext;
import org.jdiameter.common.api.app.acc.ServerAccSessionState;
import org.jdiameter.common.impl.app.acc.AppAccSessionImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Server Accounting session implementation
 *
 * @author [email protected]
 * @author  Alexandre Mendonca 
 * @author  Bartosz Baranowski 
 */
public class ServerAccSessionImpl extends AppAccSessionImpl implements EventListener, ServerAccSession, NetworkReqListener {

  private static final Logger logger = LoggerFactory.getLogger(ServerAccSessionImpl.class);

  // Factories and Listeners --------------------------------------------------
  protected transient IServerAccActionContext context;
  protected transient  ServerAccSessionListener listener;

  // Ts Timer -----------------------------------------------------------------
  protected static final String TIMER_NAME_TS = "TS";

  protected IServerAccSessionData sessionData;

  // Constructors -------------------------------------------------------------
  public ServerAccSessionImpl(IServerAccSessionData sessionData, ISessionFactory sessionFactory,
      ServerAccSessionListener serverSessionListener,
      IServerAccActionContext serverContextListener, StateChangeListener stLst, boolean stateless) {
    // TODO Auto-generated constructor stub
    this(sessionData, sessionFactory, serverSessionListener, serverContextListener, stLst);

    this.sessionData.setTsTimeout(0); // 0 == turn off
    this.sessionData.setStateless(stateless);

  }

  public ServerAccSessionImpl(IServerAccSessionData sessionData, ISessionFactory sessionFactory, ServerAccSessionListener serverSessionListener,
      IServerAccActionContext serverContextListener, StateChangeListener stLst) {
    // TODO Auto-generated constructor stub
    super(sessionFactory, sessionData);
    this.sessionData = sessionData;
    this.listener = serverSessionListener;
    this.context = serverContextListener;

    super.addStateChangeNotification(stLst);
  }

  @Override
  public void sendAccountAnswer(AccountAnswer accountAnswer) throws InternalException, IllegalStateException, RouteException, OverloadException {
    try {
      AvpSet avpSet = accountAnswer.getMessage().getAvps();
      Avp acctInterimIntervalAvp = avpSet.getAvp(Avp.ACCT_INTERIM_INTERVAL); //Unsigned32
      if (acctInterimIntervalAvp != null) {
        try {
          this.sessionData.setTsTimeout(acctInterimIntervalAvp.getUnsigned32());
        }
        catch (AvpDataException e) {
          throw new InternalException(e);
        }
      }
      cancelTsTimer();
      startTsTimer();
      session.send(accountAnswer.getMessage());
      /* TODO: Do we need to notify state change ? */
      if (isStateless() && isValid()) {
        session.release();
      }
    }
    catch (IllegalDiameterStateException e) {
      throw new IllegalStateException(e);
    }
  }

  @Override
  public boolean isStateless() {
    return sessionData.isStateless();
  }

  @SuppressWarnings("unchecked")
  protected void setState(IAppSessionState newState) {
    IAppSessionState oldState = sessionData.getServerAccSessionState();
    sessionData.setServerAccSessionState((ServerAccSessionState) newState);

    for (StateChangeListener i : stateListeners) {
      i.stateChanged(this,(Enum) oldState, (Enum) newState);
    }
  }

  @Override
  public boolean handleEvent(StateEvent event) throws InternalException, OverloadException {
    return sessionData.isStateless() ? handleEventForStatelessMode(event) : handleEventForStatefulMode(event);
  }

  public boolean handleEventForStatelessMode(StateEvent event) throws InternalException, OverloadException {
    try {
      //NOTE: NO Ts Timer here
      //this will handle RTRs as well, no need to alter.
      ServerAccSessionState state = sessionData.getServerAccSessionState();
      switch (state) {
        case IDLE: {
          switch ((Event.Type) event.getType()) {
            case RECEIVED_START_RECORD:
              // Current State: IDLE
              // Event: Accounting start request received, and successfully processed.
              // Action: Send accounting start answer
              // New State: IDLE
              if (listener != null) {
                try {
                  listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                }
                catch (Exception e) {
                  logger.debug("Can not handle event", e);
                }
              }
              // TODO: This is unnecessary state change: setState(IDLE);
              break;
            case RECEIVED_EVENT_RECORD:
              // Current State: IDLE
              // Event: Accounting event request received, and successfully processed.
              // Action: Send accounting event answer
              // New State: IDLE
              if (listener != null) {
                try {
                  listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                }
                catch (Exception e) {
                  logger.debug("Can not handle event", e);
                }
              }
              // FIXME: it is required, so we know it ends up again in IDLE!
              setState(IDLE);
              break;
            case RECEIVED_INTERIM_RECORD:
              // Current State: IDLE
              // Event: Interim record received, and successfully processed.
              // Action: Send accounting interim answer
              // New State: IDLE
              if (listener != null) {
                try {
                  listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                }
                catch (Exception e) {
                  logger.debug("Can not handle event", e);
                }
              }
              // TODO: This is unnecessary state change: setState(IDLE);
              break;
            case RECEIVED_STOP_RECORD:
              // Current State: IDLE
              // Event: Accounting stop request received, and successfully processed
              // Action: Send accounting stop answer
              // New State: IDLE
              if (listener != null) {
                try {
                  listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                }
                catch (Exception e) {
                  logger.debug("Can not handle event", e);
                }
              }
              // TODO: This is unnecessary state change: setState(IDLE);
              break;
            default:
              throw new IllegalStateException("Current state " + state + " action " + event.getType());
          }
        }
      }
    }
    catch (Exception e) {
      logger.debug("Can not process event", e);
      return false;
    }
    finally {
      // TODO: Since setState was removed, we are now using this to terminate. Correct?
      // We can't release here, answer needs to be sent through. done at send.
      // release();
    }
    return true;
  }

  public boolean handleEventForStatefulMode(StateEvent event) throws InternalException, OverloadException {
    try {
      if (((AccountRequest) event.getData()).getMessage().isReTransmitted()) {
        try {
          cancelTsTimer();
          startTsTimer();
          setState(OPEN);
          listener.doAccRequestEvent(this, (AccountRequest) event.getData());

          if (context != null) {
            context.sessionTimerStarted(this, null);
          }
        }
        catch (Exception e) {
          logger.debug("Can not handle event", e);
          setState(IDLE);
        }
        return true;
      }
      else {
        ServerAccSessionState state = sessionData.getServerAccSessionState();
        AccountRequest request =  (AccountRequest) event.getData();
        AvpSet avpSet = request.getMessage().getAvps();
        Avp acctInterimIntervalAvp = avpSet.getAvp(85);//Unsigned32
        if (acctInterimIntervalAvp != null) {
          this.sessionData.setTsTimeout(acctInterimIntervalAvp.getUnsigned32());
        }
        switch (state) {
          case IDLE: {
            switch ((Event.Type) event.getType()) {
              case RECEIVED_START_RECORD:
                // Current State: IDLE
                // Event: Accounting start request received, and successfully processed.
                // Action: Send accounting start answer, Start Ts
                // New State: OPEN
                setState(OPEN);
                if (listener != null) {
                  try {
                    listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                    cancelTsTimer();
                    startTsTimer();
                    if (context != null) {
                      context.sessionTimerStarted(this, null);
                    }
                  }
                  catch (Exception e) {
                    logger.debug("Can not handle event", e);
                    setState(IDLE);
                  }
                }
                break;
              case RECEIVED_EVENT_RECORD:
                // Current State: IDLE
                // Event: Accounting event request received, and
                // successfully processed.
                // Action: Send accounting event answer
                // New State: IDLE
                if (listener != null) {
                  try {
                    listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                  }
                  catch (Exception e) {
                    logger.debug("Can not handle event", e);
                  }
                }
                break;
            }
            break;
          }
          case OPEN: {
            switch ((Event.Type) event.getType()) {
              case RECEIVED_INTERIM_RECORD:
                // Current State: OPEN
                // Event: Interim record received, and successfully
                // processed.
                // Action: Send accounting interim answer, Restart Ts
                // New State: OPEN
                try {
                  listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                  cancelTsTimer();
                  startTsTimer();
                  if (context != null) {
                    context.sessionTimerStarted(this, null);
                  }
                }
                catch (Exception e) {
                  logger.debug("Can not handle event", e);
                  setState(IDLE);
                }
                break;
              case RECEIVED_STOP_RECORD:
                // Current State: OPEN
                // Event: Accounting stop request received, and
                // successfully
                // processed
                // Action: Send accounting stop answer, Stop Ts
                // New State: IDLE
                try {
                  setState(IDLE);
                  cancelTsTimer();
                  listener.doAccRequestEvent(this, (AccountRequest) event.getData());
                  if (context != null) {
                    context.sessionTimerCanceled(this, null);
                  }
                }
                catch (Exception e) {
                  logger.debug("Can not handle event", e);
                  setState(IDLE);
                }
                break;
            }
            break;
          }
        }
      }
    }
    catch (Exception e) {
      logger.debug("Can not process event", e);
      return false;
    }
    return true;
  }

  private void startTsTimer() {

    try {
      sendAndStateLock.lock();
      if (sessionData.getTsTimeout() > 0) {
        Serializable tsTid = super.timerFacility.schedule(sessionData.getSessionId(), TIMER_NAME_TS, sessionData.getTsTimeout());
        sessionData.setTsTimerId(tsTid);
      }
      return;
    }
    finally {
      sendAndStateLock.unlock();
    }
  }

  private void cancelTsTimer() {
    try {
      sendAndStateLock.lock();
      Serializable tsTid = sessionData.getTsTimerId();
      if (tsTid != null) {
        super.timerFacility.cancel(tsTid);
        sessionData.setTsTimerId(null);
      }
    }
    finally {
      sendAndStateLock.unlock();
    }
  }

  /* (non-Javadoc)
   * @see org.jdiameter.common.impl.app.AppSessionImpl#onTimer(java.lang.String)
   */
  @Override
  public void onTimer(String timerName) {
    if (timerName.equals(IDLE_SESSION_TIMER_NAME)) {
      checkIdleAppSession();
    }
    else if (timerName.equals(TIMER_NAME_TS)) {
      if (context != null) {
        try {
          context.sessionTimeoutElapses(ServerAccSessionImpl.this);
        }
        catch (InternalException e) {
          logger.debug("Failure on processing expired Ts", e);
        }
      }
      setState(IDLE);
    }
    else {
      logger.warn("Received an unknown timer '{}' for Session-ID '{}'", timerName, getSessionId());
    }
  }

  protected Answer createStopAnswer(Request request) {
    Answer answer = request.createAnswer(ResultCode.SUCCESS);
    answer.getAvps().addAvp(Avp.ACC_RECORD_TYPE, 4);
    answer.getAvps().addAvp(request.getAvps().getAvp(Avp.ACC_RECORD_NUMBER));
    return answer;
  }

  protected Answer createInterimAnswer(Request request) {
    Answer answer = request.createAnswer(ResultCode.SUCCESS);
    answer.getAvps().addAvp(Avp.ACC_RECORD_TYPE, 3);
    answer.getAvps().addAvp(request.getAvps().getAvp(Avp.ACC_RECORD_NUMBER));
    return answer;
  }

  protected Answer createEventAnswer(Request request) {
    Answer answer = request.createAnswer(ResultCode.SUCCESS);
    answer.getAvps().addAvp(Avp.ACC_RECORD_TYPE, 2);
    answer.getAvps().addAvp(request.getAvps().getAvp(Avp.ACC_RECORD_NUMBER));
    return answer;
  }

  protected Answer createStartAnswer(Request request) {
    Answer answer = request.createAnswer(ResultCode.SUCCESS);
    answer.getAvps().addAvp(Avp.ACC_RECORD_TYPE, 1);
    answer.getAvps().addAvp(request.getAvps().getAvp(Avp.ACC_RECORD_NUMBER));
    return answer;
  }

  @Override
  @SuppressWarnings("unchecked")
  public  E getState(Class eClass) {
    return eClass == ServerAccSessionState.class ? (E) sessionData.getServerAccSessionState() : null;
  }

  @Override
  public Answer processRequest(Request request) {
    if (request.getCommandCode() == AccountRequest.code) {
      try {
        sendAndStateLock.lock();
        handleEvent(new Event(createAccountRequest(request)));
      }
      catch (Exception e) {
        logger.debug("Can not handle event", e);
      }
      finally {
        sendAndStateLock.unlock();
      }
    }
    else {
      try {
        listener.doOtherEvent(this, createAccountRequest(request), null);
      }
      catch (Exception e) {
        logger.debug("Can not handle event", e);
      }
    }
    return null;
  }

  @Override
  public void receivedSuccessMessage(Request request, Answer answer) {
    if (request.getCommandCode() == AccountRequest.code) {
      try {
        sendAndStateLock.lock();
        handleEvent(new Event(createAccountRequest(request)));
      }
      catch (Exception e) {
        logger.debug("Can not handle event", e);
      }
      finally {
        sendAndStateLock.unlock();
      }

      try {
        listener.doAccRequestEvent(this, createAccountRequest(request));
      }
      catch (Exception e) {
        logger.debug("Can not handle event", e);
      }
    }
    else {
      try {
        listener.doOtherEvent(this, createAccountRequest(request), createAccountAnswer(answer));
      }
      catch (Exception e) {
        logger.debug("Can not handle event", e);
      }
    }
  }

  @Override
  public void timeoutExpired(Request request) {
    // FIXME: alexandre: We don't do anything here... are we even getting this on server?
  }

  /* (non-Javadoc)
   * @see org.jdiameter.common.impl.app.AppSessionImpl#isReplicable()
   */
  @Override
  public boolean isReplicable() {
    return true;
  }

  @Override
  public void release() {
    if (isValid()) {
      try {
        sendAndStateLock.lock();
        super.release();
      }
      catch (Exception e) {
        logger.debug("Failed to release session", e);
      }
      finally {
        sendAndStateLock.unlock();
      }
    }
    else {
      logger.debug("Trying to release an already invalid session, with Session ID '{}'", getSessionId());
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy