/** * Starts a new transaction on the database. Note, that transaction can also be nested. So in * order to work properly ALWAYS acquire a {@link DatabaseAdapter} instance via the {@link * DatabaseAdapter#getInstance(Context)} method. * * @return Current {@link DatabaseAdapter} instance. */ public DatabaseAdapter beginTransaction() { open(); mDb.beginTransactionWithListener(TransactionListener.getFor(this)); return this; }
private void transition(SplitTransactionPhase nextPhase, boolean isRollback) throws IOException { if (!isRollback) { // Add to the journal first, because if the listener throws an exception // we need to roll back starting at 'nextPhase' this.journal.add(new JournalEntryImpl(nextPhase)); } for (int i = 0; i < listeners.size(); i++) { TransactionListener listener = listeners.get(i); if (!isRollback) { listener.transition(this, currentPhase, nextPhase); } else { listener.rollback(this, currentPhase, nextPhase); } } currentPhase = nextPhase; }
@Override public void registerTransactionListener(final TransactionListener listener) throws BCSAPIException { try { addTopicListener( "transaction", listener, bytes -> { APITransaction transaction = null; try { transaction = APITransaction.fromProtobuf(BCSAPIMessage.TX.parseFrom(bytes)); } catch (Exception e) { log.error("Transaction message error", e); } return transaction; }, transaction -> { if (transaction != null) try { listener.process(transaction); } catch (HyperLedgerException e) { log.error("Error in transaction processing", e); } }); } catch (ConnectorException e) { throw new BCSAPIException(e); } }
@Override public void fromApp(final SipResponse res, final TransactionListener listener) { Preconditions.checkNotNull(res); if ((res.getStatus().getCode() / 100) == 1) { switch (this.state) { case Trying: this.response = res; this.setState(State.Proceeding); listener.sendToNetwork(this.flowId, this.response); break; case Proceeding: this.response = res; listener.sendToNetwork(this.flowId, this.response); break; case Initial: case Completed: case Terminated: break; } } else if ((res.getStatus().getCode() / 100) >= 2) { // schedule the expiry timer. // TODO change to dynamic value. listener.schedule(this.getBranchId(), Duration.ofSeconds(32)); switch (this.state) { case Trying: this.response = res; this.setState(State.Completed); listener.sendToNetwork(this.flowId, this.response); break; case Proceeding: this.response = res; this.setState(State.Completed); listener.sendToNetwork(this.flowId, this.response); break; case Initial: case Completed: case Terminated: break; } } }
/** * Notify all listeners that a rollback is about to occur. If a listener throws an exception then * no further listeners will be notified and a StandardException with shutdown database(?) * severity will be thrown. * * @throws StandardException */ public void preRollbackNotify() throws StandardException { if (listeners.isEmpty()) return; for (Iterator i = listeners.iterator(); i.hasNext(); ) { TransactionListener listener = (TransactionListener) i.next(); try { listener.preRollback(); i.remove(); } catch (StandardException se) { // TODO: Define behaviour on exception during rollback. if (se.getSeverity() < ExceptionSeverity.TRANSACTION_SEVERITY) {} throw se; } } }
/** * Notify all listeners that a commit is about to occur. If a listener throws an exception then no * further listeners will be notified and a StandardException with rollback severity will be * thrown. * * @throws StandardException */ public void preCommitNotify() throws StandardException { if (listeners.isEmpty()) return; for (Iterator i = listeners.iterator(); i.hasNext(); ) { TransactionListener listener = (TransactionListener) i.next(); try { if (listener.preCommit()) i.remove(); } catch (StandardException se) { // This catches any exceptions that have Transaction severity // or less (e.g. Statement exception). // If we received any lesser // error then we abort the transaction anyway. if (se.getSeverity() < ExceptionSeverity.TRANSACTION_SEVERITY) { throw StandardException.newException(SQLState.XACT_COMMIT_EXCEPTION, se); } throw se; } } }
@Override public void fromNetwork(final SipRequestReceivedEvent e, final TransactionListener listener) { switch (this.state) { case Initial: // pass the message on to the app layer. this.flowId = e.getFlowId(); this.setState(State.Trying); listener.sendToApp(null, e.getMessage()); break; case Trying: break; case Proceeding: case Completed: // always send the response down the same flow as we received it. Preconditions.checkNotNull(this.response); listener.sendToNetwork(e.getFlowId(), this.response); break; case Terminated: break; } }
@Override public void spendingTransactions(List<TID> tids, final TransactionListener listener) throws BCSAPIException { try (ConnectorSession session = connection.createSession()) { ConnectorMessage m = session.createMessage(); ConnectorProducer scanAccountProducer = session.createProducer(session.createQueue("spendingTransactions")); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder(); for (TID tid : tids) { builder.addHash(ByteString.copyFrom(tid.unsafeGetArray())); } m.setPayload(builder.build().toByteArray()); final ConnectorTemporaryQueue answerQueue = session.createTemporaryQueue(); final ConnectorConsumer consumer = session.createConsumer(answerQueue); m.setReplyTo(answerQueue); final Semaphore ready = new Semaphore(0); consumer.setMessageListener( message -> { try { byte[] body = message.getPayload(); if (body != null) { APITransaction t = APITransaction.fromProtobuf(BCSAPIMessage.TX.parseFrom(body)); listener.process(t); } else { consumer.close(); answerQueue.delete(); ready.release(); } } catch (ConnectorException | HyperLedgerException | InvalidProtocolBufferException e) { log.error("Malformed message received for spendingt ransactions request", e); } }); scanAccountProducer.send(m); ready.acquireUninterruptibly(); } catch (ConnectorException e) { throw new BCSAPIException(e); } }
private void scanRequest( Collection<Script> match, final TransactionListener listener, String requestQueue) throws BCSAPIException { try (ConnectorSession session = connection.createSession()) { ConnectorMessage m = session.createMessage(); ConnectorProducer exactMatchProducer = session.createProducer(session.createQueue(requestQueue)); BCSAPIMessage.ExactMatchRequest.Builder builder = BCSAPIMessage.ExactMatchRequest.newBuilder(); for (Script d : match) { builder.addMatch(ByteString.copyFrom(d.toByteArray())); } m.setPayload(builder.build().toByteArray()); final ConnectorTemporaryQueue answerQueue = session.createTemporaryQueue(); final ConnectorConsumer consumer = session.createConsumer(answerQueue); m.setReplyTo(answerQueue); final Semaphore ready = new Semaphore(0); consumer.setMessageListener( message -> { try { byte[] body = message.getPayload(); if (body != null) { APITransaction t = APITransaction.fromProtobuf(BCSAPIMessage.TX.parseFrom(body)); listener.process(t); } else { consumer.close(); answerQueue.delete(); ready.release(); } } catch (ConnectorException | HyperLedgerException | InvalidProtocolBufferException e) { log.error("Malformed message received for scan matching transactions", e); } }); exactMatchProducer.send(m); ready.acquireUninterruptibly(); } catch (ConnectorException e) { throw new BCSAPIException(e); } }
private void scanRequest( MasterPublicKey master, int lookAhead, final TransactionListener listener, String request) throws BCSAPIException { try (ConnectorSession session = connection.createSession()) { ConnectorMessage m = session.createMessage(); ConnectorProducer scanAccountProducer = session.createProducer(session.createQueue(request)); BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder(); builder.setPublicKey(master.serialize(true)); builder.setLookAhead(lookAhead); m.setPayload(builder.build().toByteArray()); final ConnectorTemporaryQueue answerQueue = session.createTemporaryQueue(); final ConnectorConsumer consumer = session.createConsumer(answerQueue); m.setReplyTo(answerQueue); final Semaphore ready = new Semaphore(0); consumer.setMessageListener( message -> { try { byte[] body = message.getPayload(); if (body != null) { APITransaction t = APITransaction.fromProtobuf(BCSAPIMessage.TX.parseFrom(body)); listener.process(t); } else { consumer.close(); answerQueue.delete(); ready.release(); } } catch (ConnectorException | HyperLedgerException | InvalidProtocolBufferException e) { log.error("Malformed message received for account scan transactions", e); } }); scanAccountProducer.send(m); ready.acquireUninterruptibly(); } catch (ConnectorException e) { throw new BCSAPIException(e); } }