PageRenderTime 32ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/server/impl/app/gx/ServerGxSessionImpl.java

http://mobicents.googlecode.com/
Java | 550 lines | 358 code | 57 blank | 135 comment | 40 complexity | 2846ba1fbaac9a9254f9e0cea21c33dc 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 2010, 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.gx;
  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.RouteException;
  36. import org.jdiameter.api.app.AppAnswerEvent;
  37. import org.jdiameter.api.app.AppEvent;
  38. import org.jdiameter.api.app.AppRequestEvent;
  39. import org.jdiameter.api.app.AppSession;
  40. import org.jdiameter.api.app.StateChangeListener;
  41. import org.jdiameter.api.app.StateEvent;
  42. import org.jdiameter.api.gx.events.GxReAuthRequest;
  43. import org.jdiameter.api.gx.events.GxReAuthAnswer;
  44. import org.jdiameter.api.gx.ServerGxSession;
  45. import org.jdiameter.api.gx.ServerGxSessionListener;
  46. import org.jdiameter.api.gx.events.GxCreditControlAnswer;
  47. import org.jdiameter.api.gx.events.GxCreditControlRequest;
  48. import org.jdiameter.client.api.ISessionFactory;
  49. import org.jdiameter.common.api.app.IAppSessionState;
  50. import org.jdiameter.common.api.app.gx.IGxMessageFactory;
  51. import org.jdiameter.common.api.app.gx.IServerGxSessionContext;
  52. import org.jdiameter.common.api.app.gx.ServerGxSessionState;
  53. import org.jdiameter.common.impl.app.AppAnswerEventImpl;
  54. import org.jdiameter.common.impl.app.AppRequestEventImpl;
  55. import org.jdiameter.common.impl.app.gx.AppGxSessionImpl;
  56. import org.slf4j.Logger;
  57. import org.slf4j.LoggerFactory;
  58. /**
  59. * Credit Control Application Server session implementation
  60. *
  61. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  62. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  63. * @author <a href="mailto:carl-magnus.bjorkell@emblacom.com"> Carl-Magnus Bj??rkell </a>
  64. */
  65. public class ServerGxSessionImpl extends AppGxSessionImpl implements ServerGxSession, NetworkReqListener, EventListener<Request, Answer> {
  66. private static final Logger logger = LoggerFactory.getLogger(ServerGxSessionImpl.class);
  67. // Session State Handling ---------------------------------------------------
  68. protected Lock sendAndStateLock = new ReentrantLock();
  69. // Factories and Listeners --------------------------------------------------
  70. protected transient IGxMessageFactory factory = null;
  71. protected transient IServerGxSessionContext context = null;
  72. protected transient ServerGxSessionListener listener = null;
  73. protected static final String TCC_TIMER_NAME = "TCC_GxSERVER_TIMER";
  74. protected long[] authAppIds = new long[]{4};
  75. //protected String originHost, originRealm;
  76. protected IServerGxSessionData sessionData;
  77. public ServerGxSessionImpl(IServerGxSessionData sessionData, IGxMessageFactory fct, ISessionFactory sf, ServerGxSessionListener lst, IServerGxSessionContext ctx, StateChangeListener<AppSession> stLst) {
  78. super(sf,sessionData);
  79. if (lst == null) {
  80. throw new IllegalArgumentException("Listener can not be null");
  81. }
  82. if (fct.getApplicationIds() == null) {
  83. throw new IllegalArgumentException("ApplicationId can not be less than zero");
  84. }
  85. context = ctx;
  86. authAppIds = fct.getApplicationIds();
  87. listener = lst;
  88. factory = fct;
  89. this.sessionData = sessionData;
  90. super.addStateChangeNotification(stLst);
  91. }
  92. public void sendCreditControlAnswer(GxCreditControlAnswer answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException {
  93. handleEvent(new Event(false, null, answer));
  94. }
  95. public void sendGxReAuthRequest(GxReAuthRequest request) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException {
  96. send(Event.Type.SENT_RAR, request, null);
  97. }
  98. public boolean isStateless() {
  99. return this.sessionData.isStateless();
  100. }
  101. @SuppressWarnings("unchecked")
  102. public <E> E getState(Class<E> stateType) {
  103. return stateType == ServerGxSessionState.class ? (E) this.sessionData.getServerGxSessionState() : null;
  104. }
  105. public boolean handleEvent(StateEvent event) throws InternalException, OverloadException {
  106. ServerGxSessionState newState = null;
  107. try {
  108. sendAndStateLock.lock();
  109. // Can be null if there is no state transition, transition to IDLE state should terminate this app session
  110. final Event localEvent = (Event) event;
  111. final ServerGxSessionState state = this.sessionData.getServerGxSessionState();
  112. //Its kind of awkward, but with two state on server side its easier to go through event types?
  113. //but for sake of FSM readability
  114. final Event.Type eventType = (Event.Type) localEvent.getType();
  115. switch(state)
  116. {
  117. case IDLE:
  118. switch(eventType)
  119. {
  120. case RECEIVED_INITIAL:
  121. listener.doCreditControlRequest(this, (GxCreditControlRequest)localEvent.getRequest());
  122. break;
  123. case RECEIVED_EVENT:
  124. // Current State: IDLE
  125. // Event: CC event request received and successfully processed
  126. // Action: Send CC event answer
  127. // New State: IDLE
  128. listener.doCreditControlRequest(this, (GxCreditControlRequest)localEvent.getRequest());
  129. break;
  130. case SENT_EVENT_RESPONSE:
  131. // Current State: IDLE
  132. // Event: CC event request received and successfully processed
  133. // Action: Send CC event answer
  134. // New State: IDLE
  135. // Current State: IDLE
  136. // Event: CC event request received but not successfully processed
  137. // Action: Send CC event answer with Result-Code != SUCCESS
  138. // New State: IDLE
  139. newState = ServerGxSessionState.IDLE;
  140. dispatchEvent(localEvent.getAnswer());
  141. setState(newState);
  142. break;
  143. case SENT_INITIAL_RESPONSE:
  144. GxCreditControlAnswer answer = (GxCreditControlAnswer) localEvent.getAnswer();
  145. try {
  146. long resultCode = answer.getResultCodeAvp().getUnsigned32();
  147. // Current State: IDLE
  148. // Event: CC initial request received and successfully processed
  149. // Action: Send CC initial answer, reserve units, start Tcc
  150. // New State: OPEN
  151. if(isSuccess(resultCode)) {
  152. startTcc(answer.getValidityTimeAvp());
  153. newState = ServerGxSessionState.OPEN;
  154. }
  155. // Current State: IDLE
  156. // Event: CC initial request received but not successfully processed
  157. // Action: Send CC initial answer with Result-Code != SUCCESS
  158. // New State: IDLE
  159. else {
  160. newState = ServerGxSessionState.IDLE;
  161. }
  162. dispatchEvent(localEvent.getAnswer());
  163. setState(newState);
  164. }
  165. catch (AvpDataException e) {
  166. throw new InternalException(e);
  167. }
  168. break;
  169. default:
  170. throw new InternalException("Wrong state: " + ServerGxSessionState.IDLE + " one event: " + eventType + " " + localEvent.getRequest() + " " + localEvent.getAnswer());
  171. }
  172. case OPEN:
  173. switch(eventType)
  174. {
  175. /* This should not happen, it should be silently discarded, right?
  176. case RECEIVED_INITIAL:
  177. // only for rtr
  178. if(((RoRequest)localEvent.getRequest()).getMessage().isReTransmitted()) {
  179. listener.doCreditControlRequest(this, (RoRequest)localEvent.getRequest());
  180. }
  181. else {
  182. //do nothing?
  183. }
  184. break;
  185. */
  186. case RECEIVED_UPDATE:
  187. listener.doCreditControlRequest(this, (GxCreditControlRequest)localEvent.getRequest());
  188. break;
  189. case SENT_UPDATE_RESPONSE:
  190. GxCreditControlAnswer answer = (GxCreditControlAnswer) localEvent.getAnswer();
  191. try {
  192. if(isSuccess(answer.getResultCodeAvp().getUnsigned32())) {
  193. // Current State: OPEN
  194. // Event: CC update request received and successfully processed
  195. // Action: Send CC update answer, debit used units, reserve new units, restart Tcc
  196. // New State: OPEN
  197. startTcc(answer.getValidityTimeAvp());
  198. }
  199. else {
  200. // Current State: OPEN
  201. // Event: CC update request received but not successfully processed
  202. // Action: Send CC update answer with Result-Code != SUCCESS, debit used units
  203. // New State: IDLE
  204. // It's a failure, we wait for Tcc to fire -- FIXME: Alexandre: Should we?
  205. }
  206. }
  207. catch (AvpDataException e) {
  208. throw new InternalException(e);
  209. }
  210. dispatchEvent(localEvent.getAnswer());
  211. break;
  212. case RECEIVED_TERMINATE:
  213. listener.doCreditControlRequest(this, (GxCreditControlRequest)localEvent.getRequest());
  214. break;
  215. case SENT_TERMINATE_RESPONSE:
  216. answer = (GxCreditControlAnswer) localEvent.getAnswer();
  217. try {
  218. // Current State: OPEN
  219. // Event: CC termination request received and successfully processed
  220. // Action: Send CC termination answer, Stop Tcc, debit used units
  221. // New State: IDLE
  222. if(isSuccess(answer.getResultCodeAvp().getUnsigned32())) {
  223. stopTcc(false);
  224. }
  225. else {
  226. // Current State: OPEN
  227. // Event: CC termination request received but not successfully processed
  228. // Action: Send CC termination answer with Result-Code != SUCCESS, debit used units
  229. // New State: IDLE
  230. // It's a failure, we wait for Tcc to fire -- FIXME: Alexandre: Should we?
  231. }
  232. }
  233. catch (AvpDataException e) {
  234. throw new InternalException(e);
  235. }
  236. newState = ServerGxSessionState.IDLE;
  237. dispatchEvent(localEvent.getAnswer());
  238. setState(newState);
  239. break;
  240. case RECEIVED_RAA:
  241. listener.doGxReAuthAnswer(this, (GxReAuthRequest)localEvent.getRequest(), (GxReAuthAnswer)localEvent.getAnswer());
  242. break;
  243. case SENT_RAR:
  244. dispatchEvent(localEvent.getRequest());
  245. break;
  246. }
  247. }
  248. return true;
  249. }
  250. catch (Exception e) {
  251. throw new InternalException(e);
  252. }
  253. finally {
  254. sendAndStateLock.unlock();
  255. }
  256. }
  257. /*
  258. * (non-Javadoc)
  259. *
  260. * @see org.jdiameter.common.impl.app.AppSessionImpl#isReplicable()
  261. */
  262. @Override
  263. public boolean isReplicable() {
  264. return true;
  265. }
  266. private class TccScheduledTask implements Runnable {
  267. ServerGxSession session = null;
  268. private TccScheduledTask(ServerGxSession session) {
  269. super();
  270. this.session = session;
  271. }
  272. public void run() {
  273. // Current State: OPEN
  274. // Event: Session supervision timer Tcc expired
  275. // Action: Release reserved units
  276. // New State: IDLE
  277. context.sessionSupervisionTimerExpired(session);
  278. try {
  279. sendAndStateLock.lock();
  280. // tccFuture = null;
  281. sessionData.setTccTimerId(null);
  282. setState(ServerGxSessionState.IDLE);
  283. }
  284. finally {
  285. sendAndStateLock.unlock();
  286. }
  287. }
  288. }
  289. public Answer processRequest(Request request) {
  290. RequestDelivery rd = new RequestDelivery();
  291. //rd.session = (ServerGxSession) LocalDataSource.INSTANCE.getSession(request.getSessionId());
  292. rd.session = this;
  293. rd.request = request;
  294. super.scheduler.execute(rd);
  295. return null;
  296. }
  297. public void receivedSuccessMessage(Request request, Answer answer) {
  298. AnswerDelivery rd = new AnswerDelivery();
  299. rd.session = this;
  300. rd.request = request;
  301. rd.answer = answer;
  302. super.scheduler.execute(rd);
  303. }
  304. public void timeoutExpired(Request request) {
  305. context.timeoutExpired(request);
  306. //FIXME: Should we release ?
  307. }
  308. private void startTcc(Avp validityAvp) {
  309. // There is no Validity-Time
  310. //long tccTimeout;
  311. //
  312. //if(validityAvp != null) {
  313. // try {
  314. // tccTimeout = 2 * validityAvp.getUnsigned32();
  315. // }
  316. // catch (AvpDataException e) {
  317. // logger.debug("Unable to retrieve Validity-Time AVP value, using default.", e);
  318. // tccTimeout = 2 * context.getDefaultValidityTime();
  319. // }
  320. //}
  321. //else {
  322. // tccTimeout = 2 * context.getDefaultValidityTime();
  323. //}
  324. //
  325. //if(tccTimerId != null) {
  326. // stopTcc(true);
  327. // //tccFuture = super.scheduler.schedule(new TccScheduledTask(this), defaultValue, TimeUnit.SECONDS);
  328. // tccTimerId = super.timerFacility.schedule(this.sessionId, TCC_TIMER_NAME, tccTimeout * 1000);
  329. // // FIXME: this accepts Future!
  330. // context.sessionSupervisionTimerReStarted(this, null);
  331. //}
  332. //else {
  333. // //tccFuture = super.scheduler.schedule(new TccScheduledTask(this), defaultValue, TimeUnit.SECONDS);
  334. // tccTimerId = super.timerFacility.schedule(this.sessionId, TCC_TIMER_NAME, tccTimeout * 1000);
  335. // //FIXME: this accepts Future!
  336. // context.sessionSupervisionTimerStarted(this, null);
  337. //}
  338. //super.sessionDataSource.updateSession(this);
  339. }
  340. /*
  341. * (non-Javadoc)
  342. *
  343. * @see org.jdiameter.common.impl.app.AppSessionImpl#onTimer(java.lang.String)
  344. */
  345. @Override
  346. public void onTimer(String timerName) {
  347. if (timerName.equals(TCC_TIMER_NAME)) {
  348. new TccScheduledTask(this).run();
  349. }
  350. }
  351. private void stopTcc(boolean willRestart) {
  352. final Serializable tccTimerId = this.sessionData.getTccTimerId();
  353. if (tccTimerId != null) {
  354. super.timerFacility.cancel(tccTimerId);
  355. this.sessionData.setTccTimerId(null);
  356. if (!willRestart && context!=null) {
  357. context.sessionSupervisionTimerStopped(this, null);
  358. }
  359. }
  360. }
  361. protected boolean isProvisional(long resultCode) {
  362. return resultCode >= 1000 && resultCode < 2000;
  363. }
  364. protected boolean isSuccess(long resultCode) {
  365. return resultCode >= 2000 && resultCode < 3000;
  366. }
  367. protected void setState(ServerGxSessionState newState) {
  368. setState(newState, true);
  369. }
  370. @SuppressWarnings("unchecked")
  371. protected void setState(ServerGxSessionState newState, boolean release) {
  372. IAppSessionState oldState = this.sessionData.getServerGxSessionState();
  373. this.sessionData.setServerGxSessionState(newState);
  374. for (StateChangeListener i : stateListeners) {
  375. i.stateChanged(this, (Enum) oldState, (Enum) newState);
  376. }
  377. if (newState == ServerGxSessionState.IDLE) {
  378. stopTcc(false);
  379. if (release) {
  380. // NOTE: do EVERYTHING before release.
  381. this.release();
  382. }
  383. }
  384. }
  385. @Override
  386. public void release() {
  387. if (isValid()) {
  388. try {
  389. this.sendAndStateLock.lock();
  390. this.stopTcc(false);
  391. super.release();
  392. }
  393. catch (Exception e) {
  394. logger.debug("Failed to release session", e);
  395. }
  396. finally {
  397. sendAndStateLock.unlock();
  398. }
  399. }
  400. else {
  401. logger.debug("Trying to release an already invalid session, with Session ID '{}'", getSessionId());
  402. }
  403. }
  404. protected void send(Event.Type type, AppRequestEvent request, AppAnswerEvent answer) throws InternalException {
  405. try {
  406. sendAndStateLock.lock();
  407. if (type != null) {
  408. handleEvent(new Event(type, request, answer));
  409. }
  410. }
  411. catch (Exception e) {
  412. throw new InternalException(e);
  413. }
  414. finally {
  415. sendAndStateLock.unlock();
  416. }
  417. }
  418. protected void dispatchEvent(AppEvent event) throws InternalException {
  419. try {
  420. session.send(event.getMessage(), this);
  421. // Store last destination information
  422. }
  423. catch(Exception e) {
  424. throw new InternalException(e);
  425. // logger.debug("Failure trying to dispatch event", e);
  426. }
  427. }
  428. @Override
  429. public int hashCode() {
  430. final int prime = 31;
  431. int result = super.hashCode();
  432. result = prime * result + ((sessionData == null) ? 0 : sessionData.hashCode());
  433. return result;
  434. }
  435. @Override
  436. public boolean equals(Object obj) {
  437. if (this == obj)
  438. return true;
  439. if (!super.equals(obj))
  440. return false;
  441. if (getClass() != obj.getClass())
  442. return false;
  443. ServerGxSessionImpl other = (ServerGxSessionImpl) obj;
  444. if (sessionData == null) {
  445. if (other.sessionData != null)
  446. return false;
  447. }
  448. else if (!sessionData.equals(other.sessionData))
  449. return false;
  450. return true;
  451. }
  452. private class RequestDelivery implements Runnable {
  453. ServerGxSession session;
  454. Request request;
  455. public void run() {
  456. try {
  457. switch (request.getCommandCode()) {
  458. case GxCreditControlAnswer.code:
  459. handleEvent(new Event(true, factory.createCreditControlRequest(request), null));
  460. break;
  461. default:
  462. listener.doOtherEvent(session, new AppRequestEventImpl(request), null);
  463. break;
  464. }
  465. }
  466. catch (Exception e) {
  467. logger.debug("Failed to process request message", e);
  468. }
  469. }
  470. }
  471. private class AnswerDelivery implements Runnable {
  472. ServerGxSession session;
  473. Answer answer;
  474. Request request;
  475. public void run() {
  476. try {
  477. // FIXME: baranowb: add message validation here!!!
  478. // We handle CCR, STR, ACR, ASR other go into extension
  479. switch (request.getCommandCode()) {
  480. case GxReAuthRequest.code:
  481. handleEvent(new Event(Event.Type.RECEIVED_RAA, factory.createGxReAuthRequest(request), factory.createGxReAuthAnswer(answer)));
  482. break;
  483. default:
  484. listener.doOtherEvent(session, new AppRequestEventImpl(request), new AppAnswerEventImpl(answer));
  485. break;
  486. }
  487. }
  488. catch (Exception e) {
  489. logger.debug("Failed to process success message", e);
  490. }
  491. }
  492. }
  493. }