/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/server/impl/app/acc/ServerAccSessionImpl.java
Java | 523 lines | 398 code | 38 blank | 87 comment | 50 complexity | ed67782cb3fe7b1f25310282af4f6273 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
- /*
- * 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 erick.svenson@yahoo.com
- * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
- * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
- */
- public class ServerAccSessionImpl extends AppAccSessionImpl implements EventListener<Request, Answer>, 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<AppSession> 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<AppSession> 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> E getState(Class<E> 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());
- }
- }
-
- }