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

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

There is a newer version: 1.7.1-123
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2006, 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.AccountRequestImpl;
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);
  }

  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);
    }
  }

  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);
    }
  }

  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(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 {
      // FIXME: ???
    }
  }

  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;
  }

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

  public Answer processRequest(Request request) {        
    if (request.getCommandCode() == AccountRequestImpl.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;
  }

  public void receivedSuccessMessage(Request request, Answer answer) {
    if(request.getCommandCode() == AccountRequestImpl.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);
      }
    }
  }

  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