/*
(non-Javadoc)
@see javax.sip.SipListener#processTransactionTerminated(javax.sip.TransactionTerminatedEvent)
515245
public void processTransactionTerminated(final TransactionTerminatedEvent transactionTerminatedEvent) {
		Transaction eventTransaction = null;
		if(transactionTerminatedEvent.isServerTransaction()) {
			eventTransaction = transactionTerminatedEvent.getServerTransaction();
		} else {
			eventTransaction = transactionTerminatedEvent.getClientTransaction();
		}
		final Transaction transaction = eventTransaction;
		if(logger.isDebugEnabled()) {
			logger.info("transaction " + transaction + " terminated => " + transaction.getRequest());
		}

		final TransactionApplicationData tad = (TransactionApplicationData) transaction.getApplicationData();
		final String branchId = transaction.getBranchId();
		if(tad != null && tad.getSipServletMessage() != null) {
			// https://github.com/RestComm/sip-servlets/issues/107 guard against NPEon concurrent cleanup
			final SipServletMessageImpl sipServletMessageImpl = tad.getSipServletMessage();
			final MobicentsSipSessionKey sipSessionKey = sipServletMessageImpl.getSipSessionKey();
			final MobicentsSipSession sipSession = sipServletMessageImpl.getSipSession();
			getAsynchronousExecutor().execute(new Runnable() {
				public void run() {
					try {
						if(logger.isDebugEnabled()) {
							logger.info("transaction " + transaction + " terminated => " + sipServletMessageImpl);
						}

						MobicentsB2BUAHelper b2buaHelperImpl = null;
						if(sipSession != null) {
							b2buaHelperImpl = sipSession.getB2buaHelper();
						}
						if(sipSessionKey == null) {
							if(logger.isDebugEnabled()) {
								logger.debug("no sip session were returned for this key " + sipServletMessageImpl.getSipSessionKey() + " and message " + sipServletMessageImpl);
							}
						}
						ProxyBranchImpl proxyBranch = tad.getProxyBranch();
						if(proxyBranch != null) {
							proxyBranch.removeTransaction(branchId);
						}

						// Issue 1333 : B2buaHelper.getPendingMessages(linkedSession, UAMode.UAC) returns empty list
						// don't remove the transaction on terminated state for INVITE Tx because it won't be possible
						// to create the ACK on second leg for B2BUA apps
						if(sipSession != null) {
							boolean removeTx = true;
							if(b2buaHelperImpl != null && (transaction == null || transaction instanceof ClientTransaction)
									&& Request.INVITE.equals(sipServletMessageImpl.getMethod())) {
								removeTx = false;
							}
							SipContext sipContext = findSipApplication(sipSessionKey.getApplicationName());
							//the context can be null if the server is being shutdown
							if(sipContext != null) {
								MobicentsSipApplicationSession sipApplicationSession = sipSession.getSipApplicationSession();
								try {
									sipContext.enterSipApp(sipApplicationSession, sipSession, false, true);

									if(removeTx) {
										if(b2buaHelperImpl != null && tad.getSipServletMessage() instanceof SipServletRequestImpl) {
											b2buaHelperImpl.unlinkRequestInternal((SipServletRequestImpl)tad.getSipServletMessage(), false);
										}
										sipSession.removeOngoingTransaction(transaction);
										// Issue 1468 : to handle forking, we shouldn't cleanup the app data since it is needed for the forked responses
										boolean nullifyAppData = true;
										if(((SipStackImpl)((SipProvider)transactionTerminatedEvent.getSource()).getSipStack()).getMaxForkTime() > 0 && Request.INVITE.equals(sipServletMessageImpl.getMethod())) {
											nullifyAppData = false;
										}
										if(nullifyAppData) {
											tad.cleanUp();
											if(b2buaHelperImpl == null && tad.getSipServletMessage() instanceof SipServletRequestImpl) {
												sipSession.cleanDialogInformation(false);
											}
											transaction.setApplicationData(null);
										}
									} else {
										if(logger.isDebugEnabled()) {
											logger.debug("Transaction " + transaction + " not removed from session " + sipSessionKey + " because the B2BUA might still need it to create the ACK");
										}
									}
								} finally {
									sipContext.exitSipApp(sipApplicationSession, sipSession);
								}
							}

							// If it is a client transaction, do not kill the proxy session http://code.google.com/p/mobicents/issues/detail?id=1024
							tryToInvalidateSession(sipSessionKey, transactionTerminatedEvent.isServerTransaction());

						}
					} catch (Exception e) {
						logger.error("Problem handling transaction termination", e);
					}
				}
			});
		} else {
			if(logger.isDebugEnabled()) {
				logger.debug("TransactionApplicationData not available on the following request " + transaction.getRequest());
			}
			if(tad != null) {
				tad.cleanUp();
			}
			transaction.setApplicationData(null);
		}

	}
219879
public void commitTransaction ()
    {
        if (_tevent == null) {
            String errmsg = "Cannot commit: not involved in a transaction [dobj=" + this + "]";
            throw new IllegalStateException(errmsg);
        }

        // if we are nested, we decrement our nesting count rather than committing the transaction
        if (_tcount > 0) {
            _tcount--;

        } else {
            // we may actually be doing our final commit after someone already cancelled this
            // transaction, so we need to perform the appropriate action at this point
            if (_tcancelled) {
                _tevent.cancel();
            } else {
                _tevent.commit();
            }
        }
    }
192019
private Transaction onBegin()
    {
        Transaction tx;
        if (((StatelessSessionImpl) s).getTransactionCoordinator().isTransactionActive())
        {
            tx = ((StatelessSessionImpl) s).getTransaction();
        }
        else
        {
            tx = s.beginTransaction();
        }
        return tx;
    }
986963
public void startTransaction()
   {
      Long key = Long.valueOf(Thread.currentThread().getId());
      TransactionImpl tx = txs.get(key);

      if (tx == null)
      {
         TransactionImpl newTx = new TransactionImpl(key);
         tx = txs.putIfAbsent(key, newTx);
         if (tx == null)
            tx = newTx;
      }

      tx.active();
   }
616809
public Response sendTransaction(Transaction tx) {
        if (this.peers.isEmpty()) {
            throw new NoValidPeerException(String.format("chain %s has no peers", getName()));
        }

        for(Peer peer : peers) {
            try {
                return peer.sendTransaction(tx);
            } catch (PeerException exp) {
                logger.info(String.format("Failed sending transaction to peer:%s", exp.getMessage()));
            }
        }

        throw new RuntimeException("No peer available to respond");
    }
340844
public void onBranchTerminated() {
		if(outgoingRequest != null) {
			String txid = ((ViaHeader) outgoingRequest.getMessage().getHeader(ViaHeader.NAME)).getBranch();
			proxy.removeTransaction(txid);
		}
	}
1325813
public void processLocalTransactionStartedEvent(Object handle) throws ResourceException {

        final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();

        if (isTraceOn) 
        {
            if (tc.isEntryEnabled()) 
                Tr.entry(this, tc, "processLocalTransactionStartedEvent", handle);

            if (tc.isDebugEnabled()) 
            { 
                String cId = null;
                try {
                    cId = mcf.getCorrelator(this); 
                } catch (SQLException x) {
                    // will just log the exception here and ignore it since its in trace
                    Tr.debug(this, tc, "got an exception trying to get the correlator in commit, exception is: ", x);
                }

                if (cId != null) {
                    StringBuffer stbuf = new StringBuffer(200);
                    stbuf.append("Correlator: DB2, ID: ");
                    stbuf.append(cId);
                    if (xares != null) {
                        stbuf.append(" Transaction : ");
                        stbuf.append(xares);
                    }
                    stbuf.append(" BEGIN");

                    Tr.debug(this, tc, stbuf.toString());
                }
            } 
        } 

        // An application level local transaction has been requested started

        //The isValid method returns an exception if it is not valid.  This allows the
        // WSStateManager to create a more detailed message than this class could.

        ResourceException re = stateMgr.isValid(WSStateManager.LT_BEGIN);
        if (re == null) {
            if (currentAutoCommit) {
                try {
                    if (isTraceOn && tc.isDebugEnabled()) 
                        Tr.debug(this, tc, "current autocommit is true, set to false");
                    setAutoCommit(false);
                } catch (SQLException sqle) {
                    FFDCFilter.processException(sqle, getClass().getName() + ".processLocalTransactionStartedEvent",
                                                "550", this);
                    throw new DataStoreAdapterException("DSA_ERROR", sqle, getClass());
                }
            }
            // Already validated the state so just set it. 
            stateMgr.transtate = WSStateManager.LOCAL_TRANSACTION_ACTIVE;
        } else {
            throw re;
        }

        // Fill in the ConnectionEvent only if needed. 
        connEvent.recycle(ConnectionEvent.LOCAL_TRANSACTION_STARTED, null, handle);

        if (isTraceOn && tc.isEventEnabled())
            Tr.event(this, tc, 
                     "Firing LOCAL TRANSACTION STARTED event for: " + handle, this);

        //Notification of the eventListeners must happen after the state change because if the statechange
        // is illegal, we need to throw an exception.  If this exception occurs, we do not want to
        // notify the cm of the tx started because we are not allowing it to start.
        // loop through the listeners
        for (int i = 0; i < numListeners; i++) {
            // send Local Transaction Started event to the current listener
            ivEventListeners[i].localTransactionStarted(connEvent); 
        }

        // Replace ConnectionEvent caching with a single reusable instance per
        // ManagedConnection. 

        if (isTraceOn && tc.isEntryEnabled()) 
        {
            Tr.exit(this, tc, "processLocalTransactionStartedEvent", handle);
        }

    }
796418
private String registerTransactionListenerProcessor() throws InvalidArgumentException {
        logger.debug(format("Channel %s registerTransactionListenerProcessor starting", name));

        // Transaction listener is internal Block listener for transactions

        return registerBlockListener(blockEvent -> {
            HFClient lclient = client;
            if (null == lclient || shutdown) { //can happen if were not quite shutdown
                return;
            }

            final String source = blockEvent.getPeer() != null ? blockEvent.getPeer().toString() :
                    "not peer!";

            logger.debug(format("is peer %b, is filtered: %b", blockEvent.getPeer() != null, blockEvent.isFiltered()));

            final Iterable<TransactionEvent> transactionEvents = blockEvent.getTransactionEvents();

            if (transactionEvents == null || !transactionEvents.iterator().hasNext()) {

                // no transactions today we can assume it was a config or update block.

                if (isLaterBlock(blockEvent.getBlockNumber())) {
                    ServiceDiscovery lserviceDiscovery = serviceDiscovery;
                    if (null != lserviceDiscovery) {

                        client.getExecutorService().execute(() -> lserviceDiscovery.fullNetworkDiscovery(true));
                    }

                } else {

                    lclient.getExecutorService().execute(() -> {
                        try {
                            if (!shutdown) {
                                loadCACertificates(true);
                            }

                        } catch (Exception e) {
                            logger.warn(format("Channel %s failed to load certificates for an update", name), e);
                        }

                    });

                }

                return;
            }

            if (txListeners.isEmpty() || shutdown) {
                return;
            }

            for (TransactionEvent transactionEvent : blockEvent.getTransactionEvents()) {

                logger.debug(format("Channel %s got event from %s for transaction %s in block number: %d", name,
                        source, transactionEvent.getTransactionID(), blockEvent.getBlockNumber()));

                List<TL> txL = new ArrayList<>(txListeners.size() + 2);
                synchronized (txListeners) {
                    LinkedList<TL> list = txListeners.get(transactionEvent.getTransactionID());
                    if (null != list) {
                        txL.addAll(list);
                    }
                }

                for (TL l : txL) {
                    try {
                        // only if we get events from each eventhub on the channel fire the transactions event.
                        //   if (getEventHubs().containsAll(l.eventReceived(transactionEvent.getEventHub()))) {
                        if (shutdown) {
                            break;
                        }
                        if (l.eventReceived(transactionEvent)) {
                            l.fire(transactionEvent);
                        }

                    } catch (Throwable e) {
                        logger.error(e); // Don't let one register stop rest.
                    }
                }
            }
        });
    }
1153776
public final function transactionCommit()
    {
        if (1 == $this->intTransactionDepth) {
            $this->executeTransactionCommit();
        }
        if ($this->intTransactionDepth <= 0) {
            throw new Caller("The transaction commit call is called before the transaction begin was called.");
        }
        $this->intTransactionDepth--;
    }
1323985
@Override
    public void batchPrecommit(TransactionCommon currentTran)
    {
        if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
            SibTr.entry(this, tc, "batchPrecommit", currentTran);
        try
        {
            persistCompletedPrefix(currentTran);
        } catch (SIResourceException e)
        {
            //FFDC
            FFDCFilter.processException(
                                        e,
                                        "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream.batchPrecommit",
                                        "1:1911:1.110",
                                        this);

            SibTr.exception(tc, e);

            //TODO don't know what to do here - rethrow?!
        }
        if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
            SibTr.exit(tc, "batchPrecommit");
    }
677274
private void completeCommittingTransaction(TransactionId transactionId) {
    // Change ownership of the transaction to the local node and then commit it.
    completeTransaction(
        transactionId,
        TransactionState.COMMITTING,
        info -> new TransactionInfo(localMemberId, TransactionState.COMMITTING, info.participants),
        info -> info.state == TransactionState.COMMITTING && info.coordinator.equals(localMemberId),
        (id, transactional) -> transactional.commit(id))
        .whenComplete((result, error) -> {
          if (error != null) {
            error = Throwables.getRootCause(error);
            if (error instanceof TransactionException) {
              LOGGER.warn("Failed to complete transaction", error);
            } else {
              LOGGER.warn("Failed to commit transaction " + transactionId);
            }
          }
        });
  }
693950
public synchronized void abortTransaction() throws TransactionNotInProgressException
    {
        if(isInTransaction())
        {
            fireBrokerEvent(BEFORE_ROLLBACK_EVENT);
            setInTransaction(false);
            clearRegistrationLists();
            referencesBroker.removePrefetchingListeners();
            /*
            arminw:
            check if we in local tx, before do local rollback
            Necessary, because ConnectionManager may do a rollback by itself
            or in managed environments the used connection is already be closed
            */
            if(connectionManager.isInLocalTransaction()) this.connectionManager.localRollback();
            fireBrokerEvent(AFTER_ROLLBACK_EVENT);
        }
    }
1231829
public static void addTxn(TransactionImpl txn)
    {
        if (tc.isEntryEnabled()) Tr.entry(tc, "addTxn", txn);

        final ByteArray key = new ByteArray(txn.getXid().getGlobalTransactionId());

        synchronized (txnTable)
        {
            if (!txnTable.containsKey(key))
            {
                txnTable.put(key, new JCATranWrapperImpl(txn, true, false)); // @LIDB2110C
            }
        }

        if (tc.isEntryEnabled()) Tr.exit(tc, "addTxn");
    }
1118350
public void doCommit(DefaultTransactionStatus status) throws TransactionException {
		CompensatingTransactionObject txObject = (CompensatingTransactionObject) status.getTransaction();
		txObject.getHolder().getTransactionOperationManager().commit();

	}
618922
public void removeOngoingTransaction(Transaction transaction) {
		boolean removed = false;
		if(this.ongoingTransactions != null) {
			removed = this.ongoingTransactions.remove(transaction);
		}

//		if(sessionCreatingTransactionRequest != null && sessionCreatingTransactionRequest.getMessage() != null && JainSipUtils.DIALOG_CREATING_METHODS.contains(sessionCreatingTransactionRequest.getMethod())) {
//			sessionCreatingTransactionRequest = null;
//		}

		if (proxy != null) {
		    proxy.removeTransaction(transaction.getBranchId());
		}

		if(logger.isDebugEnabled()) {
			logger.debug("transaction "+ transaction +" has been removed from sip session's ongoingTransactions ? " + removed );
		}

		updateReadyToInvalidate(transaction);
		// nullify the sessionCreatingTransactionRequest only after updateReadyToInvalidate as it is used for error response checking

		final String branchId = transaction.getBranchId();
		if(sessionCreatingTransactionRequest != null &&
				 branchId != null) {

			// https://github.com/RestComm/sip-servlets/issues/101 fix for NPE happening due to concurrent behavior cleaning the transaction
			final Transaction sessionCreatingTransactionRequestTransaction = sessionCreatingTransactionRequest.getTransaction();
			String sessionCreatingTransactionRequestTransactionBranchId = null;
			if(sessionCreatingTransactionRequestTransaction != null) {
				sessionCreatingTransactionRequestTransactionBranchId = sessionCreatingTransactionRequestTransaction.getBranchId();
			}
			final String sessionCreatingTransactionRequestMethod = sessionCreatingTransactionRequest.getMethod();

			if(sessionCreatingTransactionRequestTransaction != null &&
					branchId.equals(sessionCreatingTransactionRequestTransactionBranchId)) {

				if(logger.isDebugEnabled()) {
					logger.debug("Session " + key + ": cleaning up "+ sessionCreatingTransactionRequest
							+ " since transaction " + transaction + " with branch id " + branchId
							+ " is the same as sessionCreatingRequestTransaction " +  sessionCreatingTransactionRequestTransaction
							+ " with branch id " + sessionCreatingTransactionRequestTransactionBranchId
							+ " and method " + sessionCreatingTransactionRequestMethod);
				}
				sessionCreatingTransactionRequest.cleanUp();
				// https://telestax.atlassian.net/browse/MSS-153 improve performance by cleaning the request for dialog based requests
				// or proxy case or dialog creating methods
				if(sessionCreatingDialog != null || proxy != null || JainSipUtils.DIALOG_CREATING_METHODS.contains(sessionCreatingTransactionRequestMethod)) {
					if(logger.isDebugEnabled()) {
						logger.debug("removeOngoingTransaction - sessionCreatingTransactionRequest=" + sessionCreatingTransactionRequest);
					}
					if(logger.isDebugEnabled() && sessionCreatingTransactionRequest != null) {
						logger.debug("nullifying  sessionCreatingTransactionRequest" + sessionCreatingTransactionRequest
								+ " from Session " + key);
					}
					if(logger.isDebugEnabled()) {
						logger.debug("invalidate - setting the sessionCreatingTransactionRequest to null, id:" + this.getId());
					}
					sessionCreatingTransactionRequest = null;
				}
			}
		}
	}
435760
public void setOriginatingTransactionId(byte[] t) {
        if (t != null && t.length != 4)
            throw new IllegalArgumentException("TransactionId leng must be 4 bytes, found: " + t.length);
        this.originatingTransactionId = t;

    }
275491
private <T> void runTransaction(
      final Transaction.Function<T> transactionCallback,
      final SettableApiFuture<T> resultFuture,
      final TransactionOptions options) {
    // span is intentionally not ended here. It will be ended by runTransactionAttempt on success
    // or error.
    Span span = tracer.spanBuilder("CloudFirestore.Transaction").startSpan();
    try (Scope s = tracer.withSpan(span)) {
      runTransactionAttempt(transactionCallback, resultFuture, options, span);
    }
  }