PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/client/impl/app/gx/ClientGxSessionImpl.java

http://mobicents.googlecode.com/
Java | 1288 lines | 881 code | 108 blank | 299 comment | 189 complexity | 1345f8fa8d0f1cfd5004d28bbeb5413a 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

Large files files are truncated, but you can click here to view the full file

  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.client.impl.app.gx;
  23. import java.io.Serializable;
  24. import java.util.ArrayList;
  25. import java.util.Collections;
  26. import java.util.HashSet;
  27. import java.util.Set;
  28. import java.util.concurrent.locks.Lock;
  29. import java.util.concurrent.locks.ReentrantLock;
  30. import org.jdiameter.api.Answer;
  31. import org.jdiameter.api.AvpDataException;
  32. import org.jdiameter.api.EventListener;
  33. import org.jdiameter.api.IllegalDiameterStateException;
  34. import org.jdiameter.api.InternalException;
  35. import org.jdiameter.api.Message;
  36. import org.jdiameter.api.NetworkReqListener;
  37. import org.jdiameter.api.OverloadException;
  38. import org.jdiameter.api.Request;
  39. import org.jdiameter.api.RouteException;
  40. import org.jdiameter.api.app.AppAnswerEvent;
  41. import org.jdiameter.api.app.AppEvent;
  42. import org.jdiameter.api.app.AppSession;
  43. import org.jdiameter.api.app.StateChangeListener;
  44. import org.jdiameter.api.app.StateEvent;
  45. import org.jdiameter.api.gx.events.GxReAuthAnswer;
  46. import org.jdiameter.api.gx.events.GxReAuthRequest;
  47. import org.jdiameter.api.gx.ClientGxSession;
  48. import org.jdiameter.api.gx.ClientGxSessionListener;
  49. import org.jdiameter.api.gx.events.GxCreditControlAnswer;
  50. import org.jdiameter.api.gx.events.GxCreditControlRequest;
  51. import org.jdiameter.client.api.IContainer;
  52. import org.jdiameter.client.api.ISessionFactory;
  53. import org.jdiameter.client.api.parser.IMessageParser;
  54. import org.jdiameter.client.impl.app.gx.Event.Type;
  55. import org.jdiameter.common.api.app.IAppSessionState;
  56. import org.jdiameter.common.api.app.gx.ClientGxSessionState;
  57. import org.jdiameter.common.api.app.gx.IClientGxSessionContext;
  58. import org.jdiameter.common.api.app.gx.IGxMessageFactory;
  59. import org.jdiameter.common.impl.app.AppAnswerEventImpl;
  60. import org.jdiameter.common.impl.app.AppRequestEventImpl;
  61. import org.jdiameter.common.impl.app.gx.GxReAuthAnswerImpl;
  62. import org.jdiameter.common.impl.app.gx.AppGxSessionImpl;
  63. import org.slf4j.Logger;
  64. import org.slf4j.LoggerFactory;
  65. /**
  66. * Client Credit-Control Application session implementation
  67. *
  68. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  69. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  70. * @author <a href="mailto:carl-magnus.bjorkell@emblacom.com"> Carl-Magnus Björkell </a>
  71. */
  72. public class ClientGxSessionImpl extends AppGxSessionImpl implements ClientGxSession, NetworkReqListener, EventListener<Request, Answer> {
  73. private static final Logger logger = LoggerFactory.getLogger(ClientGxSessionImpl.class);
  74. protected IClientGxSessionData sessionData;
  75. // Session State Handling ---------------------------------------------------
  76. protected Lock sendAndStateLock = new ReentrantLock();
  77. // Factories and Listeners --------------------------------------------------
  78. protected IGxMessageFactory factory;
  79. protected ClientGxSessionListener listener;
  80. protected IClientGxSessionContext context;
  81. protected IMessageParser parser;
  82. // Tx Timer -----------------------------------------------------------------
  83. protected final static String TX_TIMER_NAME = "Gx_CLIENT_TX_TIMER";
  84. protected static final long TX_TIMER_DEFAULT_VALUE = 30 * 60 * 1000; // miliseconds
  85. protected long[] authAppIds = new long[]{4};
  86. // Requested Action + Credit-Control and Direct-Debiting Failure-Handling ---
  87. static final int NON_INITIALIZED = -300;
  88. protected static final int CCFH_TERMINATE = 0;
  89. protected static final int CCFH_CONTINUE = 1;
  90. protected static final int CCFH_RETRY_AND_TERMINATE = 2;
  91. private static final int DDFH_TERMINATE_OR_BUFFER = 0;
  92. private static final int DDFH_CONTINUE = 1;
  93. // CC-Request-Type Values ---------------------------------------------------
  94. private static final int DIRECT_DEBITING = 0;
  95. private static final int REFUND_ACCOUNT = 1;
  96. private static final int CHECK_BALANCE = 2;
  97. private static final int PRICE_ENQUIRY = 3;
  98. private static final int EVENT_REQUEST = 4;
  99. // Error Codes --------------------------------------------------------------
  100. private static final long END_USER_SERVICE_DENIED = 4010;
  101. private static final long CREDIT_CONTROL_NOT_APPLICABLE = 4011;
  102. private static final long USER_UNKNOWN = 5030;
  103. private static final long DIAMETER_UNABLE_TO_DELIVER = 3002L;
  104. private static final long DIAMETER_TOO_BUSY = 3004L;
  105. private static final long DIAMETER_LOOP_DETECTED = 3005L;
  106. protected static final Set<Long> temporaryErrorCodes;
  107. static {
  108. HashSet<Long> tmp = new HashSet<Long>();
  109. tmp.add(DIAMETER_UNABLE_TO_DELIVER);
  110. tmp.add(DIAMETER_TOO_BUSY);
  111. tmp.add(DIAMETER_LOOP_DETECTED);
  112. temporaryErrorCodes = Collections.unmodifiableSet(tmp);
  113. }
  114. // Session Based Queue
  115. protected ArrayList<Event> eventQueue = new ArrayList<Event>();
  116. public ClientGxSessionImpl(IClientGxSessionData sessionData, IGxMessageFactory fct, ISessionFactory sf, ClientGxSessionListener lst, IClientGxSessionContext ctx,
  117. StateChangeListener<AppSession> stLst) {
  118. super(sf, sessionData);
  119. if (lst == null) {
  120. throw new IllegalArgumentException("Listener can not be null");
  121. }
  122. if (fct.getApplicationIds() == null) {
  123. throw new IllegalArgumentException("ApplicationId can not be less than zero");
  124. }
  125. this.context = (IClientGxSessionContext) ctx;
  126. this.authAppIds = fct.getApplicationIds();
  127. this.listener = lst;
  128. this.factory = fct;
  129. IContainer icontainer = sf.getContainer();
  130. this.parser = icontainer.getAssemblerFacility().getComponentInstance(IMessageParser.class);
  131. this.sessionData = sessionData;
  132. super.addStateChangeNotification(stLst);
  133. }
  134. protected int getLocalCCFH() {
  135. return this.sessionData.getGatheredCCFH() >= 0 ? this.sessionData.getGatheredCCFH() : context.getDefaultCCFHValue();
  136. }
  137. protected int getLocalDDFH() {
  138. return this.sessionData.getGatheredDDFH() >= 0 ? this.sessionData.getGatheredDDFH() : context.getDefaultDDFHValue();
  139. }
  140. public void sendCreditControlRequest(GxCreditControlRequest request) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException {
  141. try {
  142. extractFHAVPs(request, null);
  143. this.handleEvent(new Event(true, request, null));
  144. } catch (AvpDataException e) {
  145. throw new InternalException(e);
  146. }
  147. }
  148. public void sendGxReAuthAnswer(GxReAuthAnswer answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException {
  149. this.handleEvent(new Event(Event.Type.SEND_RAA, null, answer));
  150. }
  151. public boolean isStateless() {
  152. return false;
  153. }
  154. public boolean isEventBased() {
  155. return this.sessionData.isEventBased();
  156. }
  157. @SuppressWarnings("unchecked")
  158. public <E> E getState(Class<E> stateType) {
  159. return stateType == ClientGxSessionState.class ? (E) this.sessionData.getClientGxSessionState() : null;
  160. }
  161. public boolean handleEvent(StateEvent event) throws InternalException, OverloadException {
  162. return this.isEventBased() ? handleEventForEventBased(event) : handleEventForSessionBased(event);
  163. }
  164. protected boolean handleEventForEventBased(StateEvent event) throws InternalException, OverloadException {
  165. try {
  166. sendAndStateLock.lock();
  167. final ClientGxSessionState state = this.sessionData.getClientGxSessionState();
  168. Event localEvent = (Event) event;
  169. Event.Type eventType = (Type) localEvent.getType();
  170. switch (state) {
  171. case IDLE:
  172. switch (eventType) {
  173. case SEND_EVENT_REQUEST:
  174. // Current State: IDLE
  175. // Event: Client or device requests a one-time service
  176. // Action: Send CC event request, start Tx
  177. // New State: PENDING_E
  178. startTx((GxCreditControlRequest) localEvent.getRequest());
  179. setState(ClientGxSessionState.PENDING_EVENT);
  180. try {
  181. dispatchEvent(localEvent.getRequest());
  182. } catch (Exception e) {
  183. // This handles failure to send in PendingI state in FSM table
  184. logger.debug("Failure handling send event request", e);
  185. handleSendFailure(e, eventType, localEvent.getRequest().getMessage());
  186. }
  187. break;
  188. default:
  189. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  190. break;
  191. }
  192. break;
  193. case PENDING_EVENT:
  194. switch (eventType) {
  195. case RECEIVE_EVENT_ANSWER:
  196. AppAnswerEvent answer = (AppAnswerEvent) localEvent.getAnswer();
  197. try {
  198. long resultCode = answer.getResultCodeAvp().getUnsigned32();
  199. if (isSuccess(resultCode)) {
  200. // Current State: PENDING_E
  201. // Event: Successful CC event answer received
  202. // Action: Grant service to end user
  203. // New State: IDLE
  204. setState(ClientGxSessionState.IDLE, false);
  205. }
  206. if (isProvisional(resultCode) || isFailure(resultCode)) {
  207. handleFailureMessage((GxCreditControlAnswer) answer, (GxCreditControlRequest) localEvent.getRequest(), eventType);
  208. }
  209. deliverGxAnswer((GxCreditControlRequest) localEvent.getRequest(), (GxCreditControlAnswer) localEvent.getAnswer());
  210. } catch (AvpDataException e) {
  211. logger.debug("Failure handling received answer event", e);
  212. setState(ClientGxSessionState.IDLE, false);
  213. }
  214. break;
  215. case Tx_TIMER_FIRED:
  216. handleTxExpires(localEvent.getRequest().getMessage());
  217. break;
  218. default:
  219. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  220. break;
  221. }
  222. break;
  223. case PENDING_BUFFERED:
  224. switch (eventType) {
  225. case RECEIVE_EVENT_ANSWER:
  226. // Current State: PENDING_B
  227. // Event: Successful CC answer received
  228. // Action: Delete request
  229. // New State: IDLE
  230. setState(ClientGxSessionState.IDLE, false);
  231. this.sessionData.setBuffer(null);
  232. deliverGxAnswer((GxCreditControlRequest) localEvent.getRequest(), (GxCreditControlAnswer) localEvent.getAnswer());
  233. break;
  234. default:
  235. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  236. break;
  237. }
  238. break;
  239. default:
  240. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  241. break;
  242. }
  243. dispatch();
  244. return true;
  245. } catch (Exception e) {
  246. throw new InternalException(e);
  247. } finally {
  248. sendAndStateLock.unlock();
  249. }
  250. }
  251. protected boolean handleEventForSessionBased(StateEvent event) throws InternalException, OverloadException {
  252. try {
  253. sendAndStateLock.lock();
  254. final ClientGxSessionState state = this.sessionData.getClientGxSessionState();
  255. final Event localEvent = (Event) event;
  256. final Event.Type eventType = (Type) localEvent.getType();
  257. switch (state) {
  258. case IDLE:
  259. switch (eventType) {
  260. case SEND_INITIAL_REQUEST:
  261. // Current State: IDLE
  262. // Event: Client or device requests access/service
  263. // Action: Send CC initial request, start Tx
  264. // New State: PENDING_I
  265. startTx((GxCreditControlRequest) localEvent.getRequest());
  266. setState(ClientGxSessionState.PENDING_INITIAL);
  267. try {
  268. dispatchEvent(localEvent.getRequest());
  269. } catch (Exception e) {
  270. // This handles failure to send in PendingI state in FSM table
  271. handleSendFailure(e, eventType, localEvent.getRequest().getMessage());
  272. }
  273. break;
  274. default:
  275. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  276. break;
  277. }
  278. break;
  279. case PENDING_INITIAL:
  280. AppAnswerEvent answer = (AppAnswerEvent) localEvent.getAnswer();
  281. switch (eventType) {
  282. case RECEIVED_INITIAL_ANSWER:
  283. long resultCode = answer.getResultCodeAvp().getUnsigned32();
  284. if (isSuccess(resultCode)) {
  285. // Current State: PENDING_I
  286. // Event: Successful CC initial answer received
  287. // Action: Stop Tx
  288. // New State: OPEN
  289. stopTx();
  290. setState(ClientGxSessionState.OPEN);
  291. } else if (isProvisional(resultCode) || isFailure(resultCode)) {
  292. handleFailureMessage((GxCreditControlAnswer) answer, (GxCreditControlRequest) localEvent.getRequest(), eventType);
  293. }
  294. deliverGxAnswer((GxCreditControlRequest) localEvent.getRequest(), (GxCreditControlAnswer) localEvent.getAnswer());
  295. break;
  296. case Tx_TIMER_FIRED:
  297. handleTxExpires(localEvent.getRequest().getMessage());
  298. break;
  299. case SEND_UPDATE_REQUEST:
  300. case SEND_TERMINATE_REQUEST:
  301. // Current State: PENDING_I
  302. // Event: User service terminated
  303. // Action: Queue termination event
  304. // New State: PENDING_I
  305. // Current State: PENDING_I
  306. // Event: Change in rating condition
  307. // Action: Queue changed rating condition event
  308. // New State: PENDING_I
  309. eventQueue.add(localEvent);
  310. break;
  311. default:
  312. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  313. break;
  314. }
  315. break;
  316. case OPEN:
  317. switch (eventType) {
  318. case SEND_UPDATE_REQUEST:
  319. // Current State: OPEN
  320. // Event: Granted unit elapses and no final unit indication received
  321. // Action: Send CC update request, start Tx
  322. // New State: PENDING_U
  323. // Current State: OPEN
  324. // Event: Change in rating condition in queue
  325. // Action: Send CC update request, start Tx
  326. // New State: PENDING_U
  327. // Current State: OPEN
  328. // Event: Change in rating condition or Validity-Time elapses
  329. // Action: Send CC update request, start Tx
  330. // New State: PENDING_U
  331. // Current State: OPEN
  332. // Event: RAR received
  333. // Action: Send RAA followed by CC update request, start Tx
  334. // New State: PENDING_U
  335. startTx((GxCreditControlRequest) localEvent.getRequest());
  336. setState(ClientGxSessionState.PENDING_UPDATE);
  337. try {
  338. dispatchEvent(localEvent.getRequest());
  339. } catch (Exception e) {
  340. // This handles failure to send in PendingI state in FSM table
  341. handleSendFailure(e, eventType, localEvent.getRequest().getMessage());
  342. }
  343. break;
  344. case SEND_TERMINATE_REQUEST:
  345. // Current State: OPEN
  346. // Event: Granted unit elapses and final unit action equal to TERMINATE received
  347. // Action: Terminate end user?s service, send CC termination request
  348. // New State: PENDING_T
  349. // Current State: OPEN
  350. // Event: Service terminated in queue
  351. // Action: Send CC termination request
  352. // New State: PENDING_T
  353. // Current State: OPEN
  354. // Event: User service terminated
  355. // Action: Send CC termination request
  356. // New State: PENDING_T
  357. setState(ClientGxSessionState.PENDING_TERMINATION);
  358. try {
  359. dispatchEvent(localEvent.getRequest());
  360. } catch (Exception e) {
  361. handleSendFailure(e, eventType, localEvent.getRequest().getMessage());
  362. }
  363. break;
  364. case RECEIVED_RAR:
  365. deliverRAR((GxReAuthRequest) localEvent.getRequest());
  366. break;
  367. case SEND_RAA:
  368. try {
  369. dispatchEvent(localEvent.getAnswer());
  370. } catch (Exception e) {
  371. handleSendFailure(e, eventType, localEvent.getRequest().getMessage());
  372. }
  373. break;
  374. default:
  375. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  376. break;
  377. }
  378. break;
  379. case PENDING_UPDATE:
  380. answer = (AppAnswerEvent) localEvent.getAnswer();
  381. switch (eventType) {
  382. case RECEIVED_UPDATE_ANSWER:
  383. long resultCode = answer.getResultCodeAvp().getUnsigned32();
  384. if (isSuccess(resultCode)) {
  385. // Current State: PENDING_U
  386. // Event: Successful CC update answer received
  387. // Action: Stop Tx
  388. // New State: OPEN
  389. stopTx();
  390. setState(ClientGxSessionState.OPEN);
  391. } else if (isProvisional(resultCode) || isFailure(resultCode)) {
  392. handleFailureMessage((GxCreditControlAnswer) answer, (GxCreditControlRequest) localEvent.getRequest(), eventType);
  393. }
  394. deliverGxAnswer((GxCreditControlRequest) localEvent.getRequest(), (GxCreditControlAnswer) localEvent.getAnswer());
  395. break;
  396. case Tx_TIMER_FIRED:
  397. handleTxExpires(localEvent.getRequest().getMessage());
  398. break;
  399. case SEND_UPDATE_REQUEST:
  400. case SEND_TERMINATE_REQUEST:
  401. // Current State: PENDING_U
  402. // Event: User service terminated
  403. // Action: Queue termination event
  404. // New State: PENDING_U
  405. // Current State: PENDING_U
  406. // Event: Change in rating condition
  407. // Action: Queue changed rating condition event
  408. // New State: PENDING_U
  409. eventQueue.add(localEvent);
  410. break;
  411. case RECEIVED_RAR:
  412. deliverRAR((GxReAuthRequest) localEvent.getRequest());
  413. break;
  414. case SEND_RAA:
  415. // Current State: PENDING_U
  416. // Event: RAR received
  417. // Action: Send RAA
  418. // New State: PENDING_U
  419. try {
  420. dispatchEvent(localEvent.getAnswer());
  421. } catch (Exception e) {
  422. handleSendFailure(e, eventType, localEvent.getRequest().getMessage());
  423. }
  424. break;
  425. }
  426. break;
  427. case PENDING_TERMINATION:
  428. switch (eventType) {
  429. case SEND_UPDATE_REQUEST:
  430. try {
  431. // Current State: PENDING_T
  432. // Event: Change in rating condition
  433. // Action: -
  434. // New State: PENDING_T
  435. dispatchEvent(localEvent.getRequest());
  436. // No transition
  437. } catch (Exception e) {
  438. // This handles failure to send in PendingI state in FSM table
  439. // handleSendFailure(e, eventType);
  440. }
  441. break;
  442. case RECEIVED_TERMINATED_ANSWER:
  443. // Current State: PENDING_T
  444. // Event: Successful CC termination answer received
  445. // Action: -
  446. // New State: IDLE
  447. // Current State: PENDING_T
  448. // Event: Failure to send, temporary error, or failed answer
  449. // Action: -
  450. // New State: IDLE
  451. //FIXME: Alex broke this, setting back "true" ?
  452. //setState(ClientGxSessionState.IDLE, false);
  453. deliverGxAnswer((GxCreditControlRequest) localEvent.getRequest(), (GxCreditControlAnswer) localEvent.getAnswer());
  454. setState(ClientGxSessionState.IDLE, true);
  455. break;
  456. default:
  457. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  458. break;
  459. }
  460. break;
  461. default:
  462. // any other state is bad
  463. setState(ClientGxSessionState.IDLE, true);
  464. }
  465. dispatch();
  466. return true;
  467. } catch (Exception e) {
  468. throw new InternalException(e);
  469. } finally {
  470. sendAndStateLock.unlock();
  471. }
  472. }
  473. public Answer processRequest(Request request) {
  474. RequestDelivery rd = new RequestDelivery();
  475. rd.session = this;
  476. rd.request = request;
  477. super.scheduler.execute(rd);
  478. return null;
  479. }
  480. public void receivedSuccessMessage(Request request, Answer answer) {
  481. AnswerDelivery ad = new AnswerDelivery();
  482. ad.session = this;
  483. ad.request = request;
  484. ad.answer = answer;
  485. super.scheduler.execute(ad);
  486. }
  487. public void timeoutExpired(Request request) {
  488. if (request.getCommandCode() == GxCreditControlAnswer.code) {
  489. try {
  490. sendAndStateLock.lock();
  491. handleSendFailure(null, null, request);
  492. }
  493. catch (Exception e) {
  494. logger.debug("Failure processing timeout message for request", e);
  495. }
  496. finally {
  497. sendAndStateLock.unlock();
  498. }
  499. }
  500. }
  501. protected void startTx(GxCreditControlRequest request) {
  502. long txTimerValue = context.getDefaultTxTimerValue();
  503. if (txTimerValue < 0) {
  504. txTimerValue = TX_TIMER_DEFAULT_VALUE;
  505. }
  506. stopTx();
  507. logger.debug("Scheduling TX Timer {}", txTimerValue);
  508. //this.txFuture = scheduler.schedule(new TxTimerTask(this, request), txTimerValue, TimeUnit.SECONDS);
  509. try {
  510. this.sessionData.setTxTimerRequest((Request) request.getMessage());
  511. } catch (Exception e) {
  512. throw new IllegalArgumentException("Failed to store request.", e);
  513. }
  514. this.sessionData.setTxTimerId(this.timerFacility.schedule(this.getSessionId(), TX_TIMER_NAME, TX_TIMER_DEFAULT_VALUE));
  515. }
  516. protected void stopTx() {
  517. Serializable txTimerId = this.sessionData.getTxTimerId();
  518. if (txTimerId != null) {
  519. this.timerFacility.cancel(txTimerId);
  520. this.sessionData.setTxTimerRequest(null);
  521. this.sessionData.setTxTimerId(null);
  522. }
  523. }
  524. /* (non-Javadoc)
  525. * @see org.jdiameter.common.impl.app.AppSessionImpl#onTimer(java.lang.String)
  526. */
  527. @Override
  528. public void onTimer(String timerName) {
  529. if (timerName.equals(TX_TIMER_NAME)) {
  530. new TxTimerTask(this, this.sessionData.getTxTimerRequest()).run();
  531. }
  532. }
  533. protected void setState(ClientGxSessionState newState) {
  534. setState(newState, true);
  535. }
  536. @SuppressWarnings("unchecked")
  537. protected void setState(ClientGxSessionState newState, boolean release) {
  538. try {
  539. IAppSessionState oldState = this.sessionData.getClientGxSessionState();
  540. this.sessionData.setClientGxSessionState(newState);
  541. for (StateChangeListener i : stateListeners) {
  542. i.stateChanged(this, (Enum) oldState, (Enum) newState);
  543. }
  544. if (newState == ClientGxSessionState.IDLE) {
  545. if (release) {
  546. this.release();
  547. }
  548. stopTx();
  549. }
  550. } catch (Exception e) {
  551. if (logger.isDebugEnabled()) {
  552. logger.debug("Failure switching to state " + this.sessionData.getClientGxSessionState() + " (release=" + release + ")", e);
  553. }
  554. }
  555. }
  556. @Override
  557. public void release() {
  558. if (isValid()) {
  559. try {
  560. this.sendAndStateLock.lock();
  561. this.stopTx();
  562. super.release();
  563. }
  564. catch (Exception e) {
  565. logger.debug("Failed to release session", e);
  566. }
  567. finally {
  568. sendAndStateLock.unlock();
  569. }
  570. }
  571. else {
  572. logger.debug("Trying to release an already invalid session, with Session ID '{}'", getSessionId());
  573. }
  574. }
  575. protected void handleSendFailure(Exception e, Event.Type eventType, Message request) throws Exception {
  576. logger.debug("Failed to send message, type: {} message: {}, failure: {}", new Object[]{eventType, request, e != null ? e.getLocalizedMessage() : ""});
  577. try {
  578. // Event Based ----------------------------------------------------------
  579. final ClientGxSessionState state = this.sessionData.getClientGxSessionState();
  580. final int gatheredRequestedAction = sessionData.getGatheredRequestedAction();
  581. if (isEventBased()) {
  582. switch (state) {
  583. case PENDING_EVENT:
  584. if (gatheredRequestedAction == CHECK_BALANCE || gatheredRequestedAction == PRICE_ENQUIRY) {
  585. // Current State: PENDING_E
  586. // Event: Failure to send, temporary error, failed CC event answer received or Tx expired; requested action CHECK_BALANCE or PRICE_ENQUIRY
  587. // Action: Indicate service error
  588. // New State: IDLE
  589. setState(ClientGxSessionState.IDLE);
  590. context.indicateServiceError(this);
  591. } else if (gatheredRequestedAction == DIRECT_DEBITING) {
  592. switch (getLocalDDFH()) {
  593. case DDFH_TERMINATE_OR_BUFFER:
  594. // Current State: PENDING_E
  595. // Event: Failure to send; request action DIRECT_DEBITING; DDFH equal to TERMINATE_OR_BUFFER
  596. // Action: Store request with T-flag
  597. // New State: IDLE
  598. request.setReTransmitted(true);
  599. this.sessionData.setBuffer((Request) request);
  600. setState(ClientGxSessionState.IDLE, false);
  601. break;
  602. case DDFH_CONTINUE:
  603. // Current State: PENDING_E
  604. // Event: Failure to send, temporary error, failed CC event answer received or Tx expired; requested action DIRECT_DEBITING; DDFH equal to CONTINUE
  605. // Action: Grant service to end user
  606. // New State: IDLE
  607. context.grantAccessOnDeliverFailure(this, request);
  608. break;
  609. default:
  610. logger.warn("Invalid Direct-Debiting-Failure-Handling AVP value {}", getLocalDDFH());
  611. }
  612. } else if (gatheredRequestedAction == REFUND_ACCOUNT) {
  613. // Current State: PENDING_E
  614. // Event: Failure to send or Tx expired; requested action REFUND_ACCOUNT
  615. // Action: Store request with T-flag
  616. // New State: IDLE
  617. setState(ClientGxSessionState.IDLE, false);
  618. request.setReTransmitted(true);
  619. this.sessionData.setBuffer((Request) request);
  620. } else {
  621. logger.warn("Invalid Requested-Action AVP value {}", gatheredRequestedAction);
  622. }
  623. break;
  624. case PENDING_BUFFERED:
  625. // Current State: PENDING_B
  626. // Event: Failure to send or temporary error
  627. // Action: -
  628. // New State: IDLE
  629. setState(ClientGxSessionState.IDLE, false);
  630. this.sessionData.setBuffer(null); // FIXME: Action does not mention, but ...
  631. break;
  632. default:
  633. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  634. break;
  635. }
  636. } // Session Based --------------------------------------------------------
  637. else {
  638. switch (getLocalCCFH()) {
  639. case CCFH_CONTINUE:
  640. // Current State: PENDING_I
  641. // Event: Failure to send, or temporary error and CCFH equal to CONTINUE
  642. // Action: Grant service to end user
  643. // New State: IDLE
  644. // Current State: PENDING_U
  645. // Event: Failure to send, or temporary error and CCFH equal to CONTINUE
  646. // Action: Grant service to end user
  647. // New State: IDLE
  648. setState(ClientGxSessionState.IDLE, false);
  649. this.context.grantAccessOnDeliverFailure(this, request);
  650. break;
  651. default:
  652. // Current State: PENDING_I
  653. // Event: Failure to send, or temporary error and CCFH equal to TERMINATE or to RETRY_AND_TERMINATE
  654. // Action: Terminate end user?s service
  655. // New State: IDLE
  656. // Current State: PENDING_U
  657. // Event: Failure to send, or temporary error and CCFH equal to TERMINATE or to RETRY_AND_TERMINATE
  658. // Action: Terminate end user?s service
  659. // New State: IDLE
  660. this.context.denyAccessOnDeliverFailure(this, request);
  661. setState(ClientGxSessionState.IDLE, true);
  662. break;
  663. }
  664. }
  665. } finally {
  666. dispatch();
  667. }
  668. }
  669. protected void handleFailureMessage(final GxCreditControlAnswer event, final GxCreditControlRequest request, final Event.Type eventType) {
  670. try {
  671. // Event Based ----------------------------------------------------------
  672. final ClientGxSessionState state = this.sessionData.getClientGxSessionState();
  673. final int gatheredRequestedAction = sessionData.getGatheredRequestedAction();
  674. final Serializable txTimerId = sessionData.getTxTimerId();
  675. final long resultCode = event.getResultCodeAvp().getUnsigned32();
  676. if (isEventBased()) {
  677. switch (state) {
  678. case PENDING_EVENT:
  679. if (resultCode == END_USER_SERVICE_DENIED || resultCode == USER_UNKNOWN) {
  680. if (sessionData.getTxTimerId() != null) {
  681. // Current State: PENDING_E
  682. // Event: CC event answer received with result code END_USER_SERVICE_DENIED or USER_UNKNOWN and Tx running
  683. // Action: Terminate end user?s service
  684. // New State: IDLE
  685. context.denyAccessOnFailureMessage(this);
  686. deliverGxAnswer(request, event);
  687. setState(ClientGxSessionState.IDLE);
  688. } else if (gatheredRequestedAction == DIRECT_DEBITING && txTimerId == null) {
  689. // Current State: PENDING_E
  690. // Event: Failed answer or answer received with result code END_USER_SERVICE DENIED or USER_UNKNOWN; requested action DIRECT_DEBITING; Tx expired
  691. // Action: -
  692. // New State: IDLE
  693. setState(ClientGxSessionState.IDLE);
  694. }
  695. } else if (resultCode == CREDIT_CONTROL_NOT_APPLICABLE && gatheredRequestedAction == DIRECT_DEBITING) {
  696. // Current State: PENDING_E
  697. // Event: CC event answer received with result code CREDIT_CONTROL_NOT_APPLICABLE; requested action DIRECT_DEBITING
  698. // Action: Grant service to end user
  699. // New State: IDLE
  700. context.grantAccessOnFailureMessage(this);
  701. deliverGxAnswer(request, event);
  702. setState(ClientGxSessionState.IDLE);
  703. } else if (temporaryErrorCodes.contains(resultCode)) {
  704. if (gatheredRequestedAction == CHECK_BALANCE || gatheredRequestedAction == PRICE_ENQUIRY) {
  705. // Current State: PENDING_E
  706. // Event: Failure to send, temporary error, failed CC event answer received or Tx expired; requested action CHECK_BALANCE or PRICE_ENQUIRY
  707. // Action: Indicate service error
  708. // New State: IDLE
  709. context.indicateServiceError(this);
  710. deliverGxAnswer(request, event);
  711. setState(ClientGxSessionState.IDLE);
  712. } else if (gatheredRequestedAction == DIRECT_DEBITING) {
  713. if (getLocalDDFH() == DDFH_CONTINUE) {
  714. // Current State: PENDING_E
  715. // Event: Failure to send, temporary error, failed CC event answer received or Tx expired; requested action DIRECT_DEBITING; DDFH equal to CONTINUE
  716. // Action: Grant service to end user
  717. // New State: IDLE
  718. context.grantAccessOnFailureMessage(this);
  719. deliverGxAnswer(request, event);
  720. setState(ClientGxSessionState.IDLE);
  721. } else if (getLocalDDFH() == DDFH_TERMINATE_OR_BUFFER && txTimerId != null) {
  722. // Current State: PENDING_E
  723. // Event: Failed CC event answer received or temporary error; requested action DIRECT_DEBITING; DDFH equal to TERMINATE_OR_BUFFER and Tx running
  724. // Action: Terminate end user?s service
  725. // New State: IDLE
  726. context.denyAccessOnFailureMessage(this);
  727. deliverGxAnswer(request, event);
  728. setState(ClientGxSessionState.IDLE);
  729. }
  730. } else if (gatheredRequestedAction == REFUND_ACCOUNT) {
  731. // Current State: PENDING_E
  732. // Event: Temporary error, and requested action REFUND_ACCOUNT
  733. // Action: Store request
  734. // New State: IDLE
  735. this.sessionData.setBuffer((Request) request.getMessage());
  736. setState(ClientGxSessionState.IDLE, false);
  737. } else {
  738. logger.warn("Invalid combination for Ro Client FSM: State {}, Result-Code {}, Requested-Action {}, DDFH {}, Tx {}", new Object[]{state, resultCode, gatheredRequestedAction, getLocalDDFH(), txTimerId});
  739. }
  740. } else { // Failure
  741. if (gatheredRequestedAction == CHECK_BALANCE || gatheredRequestedAction == PRICE_ENQUIRY) {
  742. // Current State: PENDING_E
  743. // Event: Failure to send, temporary error, failed CC event answer received or Tx expired; requested action CHECK_BALANCE or PRICE_ENQUIRY
  744. // Action: Indicate service error
  745. // New State: IDLE
  746. context.indicateServiceError(this);
  747. deliverGxAnswer(request, event);
  748. setState(ClientGxSessionState.IDLE);
  749. } else if (gatheredRequestedAction == DIRECT_DEBITING) {
  750. if (getLocalDDFH() == DDFH_CONTINUE) {
  751. // Current State: PENDING_E
  752. // Event: Failure to send, temporary error, failed CC event answer received or Tx expired; requested action DIRECT_DEBITING; DDFH equal to CONTINUE
  753. // Action: Grant service to end user
  754. // New State: IDLE
  755. context.grantAccessOnFailureMessage(this);
  756. deliverGxAnswer(request, event);
  757. setState(ClientGxSessionState.IDLE);
  758. } else if (getLocalDDFH() == DDFH_TERMINATE_OR_BUFFER && txTimerId != null) {
  759. // Current State: PENDING_E
  760. // Event: Failed CC event answer received or temporary error; requested action DIRECT_DEBITING; DDFH equal to TERMINATE_OR_BUFFER and Tx running
  761. // Action: Terminate end user?s service
  762. // New State: IDLE
  763. context.denyAccessOnFailureMessage(this);
  764. deliverGxAnswer(request, event);
  765. setState(ClientGxSessionState.IDLE);
  766. }
  767. } else if (gatheredRequestedAction == REFUND_ACCOUNT) {
  768. // Current State: PENDING_E
  769. // Event: Failed CC event answer received; requested action REFUND_ACCOUNT
  770. // Action: Indicate service error and delete request
  771. // New State: IDLE
  772. this.sessionData.setBuffer(null);
  773. context.indicateServiceError(this);
  774. deliverGxAnswer(request, event);
  775. setState(ClientGxSessionState.IDLE);
  776. } else {
  777. logger.warn("Invalid combination for Ro Client FSM: State {}, Result-Code {}, Requested-Action {}, DDFH {}, Tx {}", new Object[]{state, resultCode, gatheredRequestedAction, getLocalDDFH(), txTimerId});
  778. }
  779. }
  780. break;
  781. case PENDING_BUFFERED:
  782. // Current State: PENDING_B
  783. // Event: Failed CC answer received
  784. // Action: Delete request
  785. // New State: IDLE
  786. this.sessionData.setBuffer(null);
  787. setState(ClientGxSessionState.IDLE, false);
  788. break;
  789. default:
  790. logger.warn("Wrong event type ({}) on state {}", eventType, state);
  791. }
  792. } // Session Based --------------------------------------------------------
  793. else {
  794. switch (state) {
  795. case PENDING_INITIAL:
  796. if (resultCode == CREDIT_CONTROL_NOT_APPLICABLE) {
  797. // Current State: PENDING_I
  798. // Event: CC initial answer received with result code equal to CREDIT_CONTROL_NOT_APPLICABLE
  799. // Action: Grant service to end user
  800. // New State: IDLE
  801. context.grantAccessOnFailureMessage(this);
  802. setState(ClientGxSessionState.IDLE, false);
  803. } else if ((resultCode == END_USER_SERVICE_DENIED) || (resultCode == USER_UNKNOWN)) {
  804. // Current State: PENDING_I
  805. // Event: CC initial answer received with result code END_USER_SERVICE_DENIED or USER_UNKNOWN
  806. // Action: Terminate end user?s service
  807. // New State: IDLE
  808. context.denyAccessOnFailureMessage(this);
  809. setState(ClientGxSessionState.IDLE, false);
  810. } else {
  811. // Temporary errors and others
  812. switch (getLocalCCFH()) {
  813. case CCFH_CONTINUE:
  814. // Current State: PENDING_I
  815. // Event: Failed CC initial answer received and CCFH equal to CONTINUE
  816. // Action: Grant service to end user
  817. // New State: IDLE
  818. context.grantAccessOnFailureMessage(this);
  819. setState(ClientGxSessionState.IDLE, false);
  820. break;
  821. case CCFH_TERMINATE:
  822. case CCFH_RETRY_AND_TERMINATE:
  823. // Current State: PENDING_I
  824. // Event: Failed CC initial answer received and CCFH equal to TERMINATE or to RETRY_AND_TERMINATE
  825. // Action: Terminate end user?s service
  826. // New State: IDLE
  827. context.denyAccessOnFailureMessage(this);
  828. setState(ClientGxSessionState.IDLE, false);
  829. break;
  830. default:
  831. logger.warn("Invalid value for CCFH: {}", getLocalCCFH());
  832. break;
  833. }
  834. }
  835. break;
  836. case PENDING_UPDATE:
  837. if (resultCode == CREDIT_CONTROL_NOT_APPLICABLE) {
  838. // Current State: PENDING_U
  839. // Event: CC update answer received with result code equal to CREDIT_CONTROL_NOT_APPLICABLE
  840. // Action: Grant service to end user
  841. // New State: IDLE
  842. context.grantAccessOnFailureMessage(this);
  843. setState(ClientGxSessionState.IDLE, false);
  844. } else if (resultCode == END_USER_SERVICE_DENIED) {
  845. // Current State: PENDING_U
  846. // Event: CC update answer received with result code END_USER_SERVICE_DENIED
  847. // Action: Terminate end user?s service
  848. // New State: IDLE
  849. context.denyAccessOnFailureMessage(this);
  850. setState(ClientGxSessionState.IDLE, false);
  851. } else {
  852. // Temporary errors and others
  853. switch (getLocalCCFH()) {
  854. case CCFH_CONTINUE:
  855. // Current State: PENDING_U
  856. // Event: Failed CC update answer received and CCFH equal to CONTINUE
  857. // Action: Grant service to end user
  858. // New State: IDLE
  859. context.grantAccessOnFailureMessage(this);
  860. setState(ClientGxSessionState.IDLE, false);
  861. break;
  862. case CCFH_TERMINATE:
  863. case CCFH_RETRY_AND_TERMINATE:
  864. // Current State: PENDING_U
  865. // Event: Failed CC update answer received and CCFH equal to CONTINUE or to RETRY_AND_CONTINUE
  866. // Action: Terminate end user?s service
  867. // New State: IDLE
  868. context.denyAccessOnFailureMessage(this);
  869. setState(ClientGxSessionState.IDLE, false);
  870. break;
  871. default:
  872. logger.warn("Invalid value for CCFH: " + getLocalCCFH());
  873. break;

Large files files are truncated, but you can click here to view the full file