PageRenderTime 28ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/server/impl/app/ro/ServerRoSessionImpl.java

http://mobicents.googlecode.com/
Java | 541 lines | 351 code | 56 blank | 134 comment | 31 complexity | 91d14f21e5f54916a0a11faf9ff2987c 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
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2006, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jdiameter.server.impl.app.ro;
  23. import java.io.Serializable;
  24. import java.util.concurrent.locks.Lock;
  25. import java.util.concurrent.locks.ReentrantLock;
  26. import org.jdiameter.api.Answer;
  27. import org.jdiameter.api.Avp;
  28. import org.jdiameter.api.AvpDataException;
  29. import org.jdiameter.api.EventListener;
  30. import org.jdiameter.api.IllegalDiameterStateException;
  31. import org.jdiameter.api.InternalException;
  32. import org.jdiameter.api.NetworkReqListener;
  33. import org.jdiameter.api.OverloadException;
  34. import org.jdiameter.api.Request;
  35. import org.jdiameter.api.ResultCode;
  36. import org.jdiameter.api.RouteException;
  37. import org.jdiameter.api.app.AppAnswerEvent;
  38. import org.jdiameter.api.app.AppEvent;
  39. import org.jdiameter.api.app.AppRequestEvent;
  40. import org.jdiameter.api.app.AppSession;
  41. import org.jdiameter.api.app.StateChangeListener;
  42. import org.jdiameter.api.app.StateEvent;
  43. import org.jdiameter.api.auth.events.ReAuthRequest;
  44. import org.jdiameter.api.ro.ServerRoSession;
  45. import org.jdiameter.api.ro.ServerRoSessionListener;
  46. import org.jdiameter.api.ro.events.RoCreditControlAnswer;
  47. import org.jdiameter.api.ro.events.RoCreditControlRequest;
  48. import org.jdiameter.client.api.ISessionFactory;
  49. import org.jdiameter.common.api.app.IAppSessionState;
  50. import org.jdiameter.common.api.app.ro.IRoMessageFactory;
  51. import org.jdiameter.common.api.app.ro.IServerRoSessionContext;
  52. import org.jdiameter.common.api.app.ro.ServerRoSessionState;
  53. import org.jdiameter.common.impl.app.AppAnswerEventImpl;
  54. import org.jdiameter.common.impl.app.AppRequestEventImpl;
  55. import org.jdiameter.common.impl.app.auth.ReAuthAnswerImpl;
  56. import org.jdiameter.common.impl.app.auth.ReAuthRequestImpl;
  57. import org.jdiameter.common.impl.app.ro.AppRoSessionImpl;
  58. import org.slf4j.Logger;
  59. import org.slf4j.LoggerFactory;
  60. /**
  61. * Ro Application Server session implementation
  62. *
  63. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  64. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  65. */
  66. public class ServerRoSessionImpl extends AppRoSessionImpl implements ServerRoSession, NetworkReqListener, EventListener<Request, Answer> {
  67. private static final Logger logger = LoggerFactory.getLogger(ServerRoSessionImpl.class);
  68. // Session State Handling ---------------------------------------------------
  69. protected Lock sendAndStateLock = new ReentrantLock();
  70. // Factories and Listeners --------------------------------------------------
  71. protected transient IRoMessageFactory factory = null;
  72. protected transient IServerRoSessionContext context = null;
  73. protected transient ServerRoSessionListener listener = null;
  74. protected static final String TCC_TIMER_NAME = "TCC_RoSERVER_TIMER";
  75. protected long[] authAppIds = new long[]{4};
  76. //protected String originHost, originRealm;
  77. protected IServerRoSessionData sessionData;
  78. public ServerRoSessionImpl(IServerRoSessionData sessionData, IRoMessageFactory fct, ISessionFactory sf, ServerRoSessionListener lst, IServerRoSessionContext ctx, StateChangeListener<AppSession> stLst) {
  79. super(sf,sessionData);
  80. if(sessionData == null) {
  81. throw new IllegalArgumentException("SessionData can not be null");
  82. }
  83. if (lst == null) {
  84. throw new IllegalArgumentException("Listener can not be null");
  85. }
  86. if (fct.getApplicationIds() == null) {
  87. throw new IllegalArgumentException("ApplicationId can not be less than zero");
  88. }
  89. this.sessionData = sessionData;
  90. this.context = ctx;
  91. this.authAppIds = fct.getApplicationIds();
  92. this.listener = lst;
  93. this.factory = fct;
  94. super.addStateChangeNotification(stLst);
  95. }
  96. public void sendCreditControlAnswer(RoCreditControlAnswer answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException {
  97. handleEvent(new Event(false, null, answer));
  98. }
  99. public void sendReAuthRequest(ReAuthRequest request) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException {
  100. send(Event.Type.SENT_RAR, request, null);
  101. }
  102. public boolean isStateless() {
  103. return this.sessionData.isStateless();
  104. }
  105. @SuppressWarnings("unchecked")
  106. public <E> E getState(Class<E> stateType) {
  107. return stateType == ServerRoSessionState.class ? (E) this.sessionData.getServerRoSessionState() : null;
  108. }
  109. public boolean handleEvent(StateEvent event) throws InternalException, OverloadException {
  110. ServerRoSessionState newState = null;
  111. ServerRoSessionState state = sessionData.getServerRoSessionState();
  112. try {
  113. sendAndStateLock.lock();
  114. // Can be null if there is no state transition, transition to IDLE state should terminate this app session
  115. Event localEvent = (Event) event;
  116. //Its kind of awkward, but with two state on server side its easier to go through event types?
  117. //but for sake of FSM readability
  118. Event.Type eventType = (Event.Type) localEvent.getType();
  119. switch(state)
  120. {
  121. case IDLE:
  122. switch(eventType)
  123. {
  124. case RECEIVED_INITIAL:
  125. listener.doCreditControlRequest(this, (RoCreditControlRequest)localEvent.getRequest());
  126. break;
  127. case RECEIVED_EVENT:
  128. // Current State: IDLE
  129. // Event: CC event request received and successfully processed
  130. // Action: Send CC event answer
  131. // New State: IDLE
  132. listener.doCreditControlRequest(this, (RoCreditControlRequest)localEvent.getRequest());
  133. break;
  134. case SENT_EVENT_RESPONSE:
  135. // Current State: IDLE
  136. // Event: CC event request received and successfully processed
  137. // Action: Send CC event answer
  138. // New State: IDLE
  139. // Current State: IDLE
  140. // Event: CC event request received but not successfully processed
  141. // Action: Send CC event answer with Result-Code != SUCCESS
  142. // New State: IDLE
  143. newState = ServerRoSessionState.IDLE;
  144. dispatchEvent(localEvent.getAnswer());
  145. setState(newState);
  146. break;
  147. case SENT_INITIAL_RESPONSE:
  148. RoCreditControlAnswer answer = (RoCreditControlAnswer) localEvent.getAnswer();
  149. try {
  150. long resultCode = answer.getResultCodeAvp().getUnsigned32();
  151. // Current State: IDLE
  152. // Event: CC initial request received and successfully processed
  153. // Action: Send CC initial answer, reserve units, start Tcc
  154. // New State: OPEN
  155. if(isSuccess(resultCode)) {
  156. startTcc(answer.getValidityTimeAvp());
  157. newState = ServerRoSessionState.OPEN;
  158. }
  159. // Current State: IDLE
  160. // Event: CC initial request received but not successfully processed
  161. // Action: Send CC initial answer with Result-Code != SUCCESS
  162. // New State: IDLE
  163. else {
  164. newState = ServerRoSessionState.IDLE;
  165. }
  166. dispatchEvent(localEvent.getAnswer());
  167. setState(newState);
  168. }
  169. catch (AvpDataException e) {
  170. throw new InternalException(e);
  171. }
  172. break;
  173. case RECEIVED_UPDATE:
  174. case RECEIVED_TERMINATE:
  175. Answer errorAnswer = ((Request) localEvent.getRequest().getMessage()).createAnswer(ResultCode.UNKNOWN_SESSION_ID);
  176. session.send(errorAnswer);
  177. logger.debug("Received an UPDATE or TERMINATE for a new session. Answering with 5002 (UNKNOWN_SESSION_ID) and terminating session.");
  178. // and let it throw exception anyway ...
  179. default:
  180. throw new InternalException("Wrong state: " + ServerRoSessionState.IDLE + " one event: " + eventType + " " + localEvent.getRequest() + " " + localEvent.getAnswer());
  181. }
  182. case OPEN:
  183. switch(eventType)
  184. {
  185. /* This should not happen, it should be silently discarded, right?
  186. case RECEIVED_INITIAL:
  187. // only for rtr
  188. if(((RoRequest)localEvent.getRequest()).getMessage().isReTransmitted()) {
  189. listener.doCreditControlRequest(this, (RoRequest)localEvent.getRequest());
  190. }
  191. else {
  192. //do nothing?
  193. }
  194. break;
  195. */
  196. case RECEIVED_UPDATE:
  197. listener.doCreditControlRequest(this, (RoCreditControlRequest)localEvent.getRequest());
  198. break;
  199. case SENT_UPDATE_RESPONSE:
  200. RoCreditControlAnswer answer = (RoCreditControlAnswer) localEvent.getAnswer();
  201. try {
  202. if(isSuccess(answer.getResultCodeAvp().getUnsigned32())) {
  203. // Current State: OPEN
  204. // Event: CC update request received and successfully processed
  205. // Action: Send CC update answer, debit used units, reserve new units, restart Tcc
  206. // New State: OPEN
  207. startTcc(answer.getValidityTimeAvp());
  208. }
  209. else {
  210. // Current State: OPEN
  211. // Event: CC update request received but not successfully processed
  212. // Action: Send CC update answer with Result-Code != SUCCESS, debit used units
  213. // New State: IDLE
  214. // It's a failure, we wait for Tcc to fire -- FIXME: Alexandre: Should we?
  215. }
  216. }
  217. catch (AvpDataException e) {
  218. throw new InternalException(e);
  219. }
  220. dispatchEvent(localEvent.getAnswer());
  221. break;
  222. case RECEIVED_TERMINATE:
  223. listener.doCreditControlRequest(this, (RoCreditControlRequest)localEvent.getRequest());
  224. break;
  225. case SENT_TERMINATE_RESPONSE:
  226. answer = (RoCreditControlAnswer) localEvent.getAnswer();
  227. try {
  228. // Current State: OPEN
  229. // Event: CC termination request received and successfully processed
  230. // Action: Send CC termination answer, Stop Tcc, debit used units
  231. // New State: IDLE
  232. if(isSuccess(answer.getResultCodeAvp().getUnsigned32())) {
  233. stopTcc(false);
  234. }
  235. else {
  236. // Current State: OPEN
  237. // Event: CC termination request received but not successfully processed
  238. // Action: Send CC termination answer with Result-Code != SUCCESS, debit used units
  239. // New State: IDLE
  240. // It's a failure, we wait for Tcc to fire -- FIXME: Alexandre: Should we?
  241. }
  242. }
  243. catch (AvpDataException e) {
  244. throw new InternalException(e);
  245. }
  246. newState = ServerRoSessionState.IDLE;
  247. dispatchEvent(localEvent.getAnswer());
  248. setState(newState);
  249. break;
  250. case RECEIVED_RAA:
  251. listener.doReAuthAnswer(this, new ReAuthRequestImpl(localEvent.getRequest().getMessage()), new ReAuthAnswerImpl((Answer)localEvent.getAnswer().getMessage()));
  252. break;
  253. case SENT_RAR:
  254. dispatchEvent(localEvent.getRequest());
  255. break;
  256. }
  257. }
  258. return true;
  259. }
  260. catch (Exception e) {
  261. throw new InternalException(e);
  262. }
  263. finally {
  264. sendAndStateLock.unlock();
  265. }
  266. }
  267. /*
  268. * (non-Javadoc)
  269. *
  270. * @see org.jdiameter.common.impl.app.AppSessionImpl#isReplicable()
  271. */
  272. @Override
  273. public boolean isReplicable() {
  274. return true;
  275. }
  276. private class TccScheduledTask implements Runnable {
  277. ServerRoSession session = null;
  278. private TccScheduledTask(ServerRoSession session) {
  279. super();
  280. this.session = session;
  281. }
  282. public void run() {
  283. // Current State: OPEN
  284. // Event: Session supervision timer Tcc expired
  285. // Action: Release reserved units
  286. // New State: IDLE
  287. try {
  288. sendAndStateLock.lock();
  289. if(context != null) {
  290. context.sessionSupervisionTimerExpired(session);
  291. }
  292. }
  293. finally {
  294. try{
  295. sessionData.setTccTimerId(null);
  296. setState(ServerRoSessionState.IDLE);
  297. }
  298. catch (Exception e) {
  299. logger.error("",e);
  300. }
  301. sendAndStateLock.unlock();
  302. }
  303. }
  304. }
  305. public Answer processRequest(Request request) {
  306. RequestDelivery rd = new RequestDelivery();
  307. //rd.session = (ServerRoSession) LocalDataSource.INSTANCE.getSession(request.getSessionId());
  308. rd.session = this;
  309. rd.request = request;
  310. super.scheduler.execute(rd);
  311. return null;
  312. }
  313. public void receivedSuccessMessage(Request request, Answer answer) {
  314. AnswerDelivery rd = new AnswerDelivery();
  315. rd.session = this;
  316. rd.request = request;
  317. rd.answer = answer;
  318. super.scheduler.execute(rd);
  319. }
  320. public void timeoutExpired(Request request) {
  321. context.timeoutExpired(request);
  322. //FIXME: Should we release ?
  323. }
  324. private void startTcc(Avp validityAvp) {
  325. // There is no Validity-Time
  326. //long tccTimeout;
  327. //
  328. //if(validityAvp != null) {
  329. // try {
  330. // tccTimeout = 2 * validityAvp.getUnsigned32();
  331. // }
  332. // catch (AvpDataException e) {
  333. // logger.debug("Unable to retrieve Validity-Time AVP value, using default.", e);
  334. // tccTimeout = 2 * context.getDefaultValidityTime();
  335. // }
  336. //}
  337. //else {
  338. // tccTimeout = 2 * context.getDefaultValidityTime();
  339. //}
  340. //
  341. //if(tccTimerId != null) {
  342. // stopTcc(true);
  343. // //tccFuture = super.scheduler.schedule(new TccScheduledTask(this), defaultValue, TimeUnit.SECONDS);
  344. // tccTimerId = super.timerFacility.schedule(this.sessionId, TCC_TIMER_NAME, tccTimeout * 1000);
  345. // // FIXME: this accepts Future!
  346. // context.sessionSupervisionTimerReStarted(this, null);
  347. //}
  348. //else {
  349. // //tccFuture = super.scheduler.schedule(new TccScheduledTask(this), defaultValue, TimeUnit.SECONDS);
  350. // tccTimerId = super.timerFacility.schedule(this.sessionId, TCC_TIMER_NAME, tccTimeout * 1000);
  351. // //FIXME: this accepts Future!
  352. // context.sessionSupervisionTimerStarted(this, null);
  353. //}
  354. //super.sessionDataSource.updateSession(this);
  355. }
  356. /*
  357. * (non-Javadoc)
  358. *
  359. * @see org.jdiameter.common.impl.app.AppSessionImpl#onTimer(java.lang.String)
  360. */
  361. @Override
  362. public void onTimer(String timerName) {
  363. if (timerName.equals(TCC_TIMER_NAME)) {
  364. new TccScheduledTask(this).run();
  365. }
  366. }
  367. private void stopTcc(boolean willRestart) {
  368. Serializable tccTimerId = sessionData.getTccTimerId();
  369. if (tccTimerId != null) {
  370. // tccFuture.cancel(false);
  371. super.timerFacility.cancel(tccTimerId);
  372. // ScheduledFuture f = tccFuture;
  373. sessionData.setTccTimerId(null);
  374. if (!willRestart) {
  375. context.sessionSupervisionTimerStopped(this, null);
  376. }
  377. }
  378. }
  379. protected boolean isProvisional(long resultCode) {
  380. return resultCode >= 1000 && resultCode < 2000;
  381. }
  382. protected boolean isSuccess(long resultCode) {
  383. return resultCode >= 2000 && resultCode < 3000;
  384. }
  385. protected void setState(ServerRoSessionState newState) {
  386. setState(newState, true);
  387. }
  388. @SuppressWarnings("unchecked")
  389. protected void setState(ServerRoSessionState newState, boolean release) {
  390. IAppSessionState oldState = sessionData.getServerRoSessionState();
  391. sessionData.setServerRoSessionState(newState);
  392. for (StateChangeListener i : stateListeners) {
  393. i.stateChanged(this, (Enum) oldState, (Enum) newState);
  394. }
  395. if (newState == ServerRoSessionState.IDLE) {
  396. stopTcc(false);
  397. if (release) {
  398. this.release();
  399. }
  400. }
  401. }
  402. @Override
  403. public void release() {
  404. if (isValid()) {
  405. try {
  406. this.sendAndStateLock.lock();
  407. this.stopTcc(false);
  408. super.release();
  409. }
  410. catch (Exception e) {
  411. logger.debug("Failed to release session", e);
  412. }
  413. finally {
  414. sendAndStateLock.unlock();
  415. }
  416. }
  417. else {
  418. logger.debug("Trying to release an already invalid session, with Session ID '{}'", getSessionId());
  419. }
  420. }
  421. protected void send(Event.Type type, AppRequestEvent request, AppAnswerEvent answer) throws InternalException {
  422. try {
  423. sendAndStateLock.lock();
  424. if (type != null) {
  425. handleEvent(new Event(type, request, answer));
  426. }
  427. }
  428. catch (Exception e) {
  429. throw new InternalException(e);
  430. }
  431. finally {
  432. sendAndStateLock.unlock();
  433. }
  434. }
  435. protected void dispatchEvent(AppEvent event) throws InternalException {
  436. try {
  437. session.send(event.getMessage(), this);
  438. }
  439. catch(Exception e) {
  440. //throw new InternalException(e);
  441. logger.debug("Failure trying to dispatch event", e);
  442. }
  443. }
  444. private class RequestDelivery implements Runnable {
  445. ServerRoSession session;
  446. Request request;
  447. public void run() {
  448. try {
  449. switch (request.getCommandCode()) {
  450. case RoCreditControlAnswer.code:
  451. handleEvent(new Event(true, factory.createCreditControlRequest(request), null));
  452. break;
  453. default:
  454. listener.doOtherEvent(session, new AppRequestEventImpl(request), null);
  455. break;
  456. }
  457. }
  458. catch (Exception e) {
  459. logger.debug("Failed to process request message", e);
  460. }
  461. }
  462. }
  463. private class AnswerDelivery implements Runnable {
  464. ServerRoSession session;
  465. Answer answer;
  466. Request request;
  467. public void run() {
  468. try {
  469. // FIXME: baranowb: add message validation here!!!
  470. // We handle CCR, STR, ACR, ASR other go into extension
  471. switch (request.getCommandCode()) {
  472. case ReAuthRequest.code:
  473. handleEvent(new Event(Event.Type.RECEIVED_RAA, factory.createReAuthRequest(request), factory.createReAuthAnswer(answer)));
  474. break;
  475. default:
  476. listener.doOtherEvent(session, new AppRequestEventImpl(request), new AppAnswerEventImpl(answer));
  477. break;
  478. }
  479. }
  480. catch (Exception e) {
  481. logger.debug("Failed to process success message", e);
  482. }
  483. }
  484. }
  485. }