Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jdiameter.server.impl.app.acc.ServerAccSessionImpl Maven / Gradle / Ivy
/*
* 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
*/
@SuppressWarnings("all") //3rd party lib
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());
}
}
}