I am using Corda's Account library and currently unable to transfer a state from one owner (Account) to another (Account) hosted on separate nodes. Getting different errors at different time. But I am pretty sure it has to do with collecting signatures.
Is it ok to collect signature from owner account (anonymous party) or does it have to be the nodes owning key. Also, is it mandatory to collect signatures from all parties involved in the participants list in states class. For instance in the below example, is it necessary to collect signature from issue during a transfer flow.
An Issuer issues a state to an owner using Registration Flow.
An owner can transfer to another owner using Transfer Flow.
I am currently having issues with Transfer Flow.
Transfer Flow
package com.template.flows;
import co.paralleluniverse.fibers.Suspendable;
import com.r3.corda.lib.accounts.contracts.states.AccountInfo;
import com.r3.corda.lib.accounts.workflows.UtilitiesKt;
import com.r3.corda.lib.accounts.workflows.flows.RequestKeyForAccount;
import com.template.accountutilities.NewKeyForAccount;
import com.template.contracts.CarContract;
import com.template.states.CarState;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndRef;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.flows.*;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.Party;
import net.corda.core.node.services.Vault;
import net.corda.core.node.services.vault.QueryCriteria;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import static com.template.contracts.CarContract.CID;
import java.lang.reflect.Array;
import java.security.PublicKey;
import java.util.*;
import java.util.stream.Collectors;
#InitiatingFlow
#StartableByRPC
public class CarTransferFlowInitiator extends FlowLogic<String> {
private final String carVin;
private final String oldCarOwner;
private final String newCarOwner;
private int input;
public CarTransferFlowInitiator(String carVin,String oldCarOwner, String newCarOwner){
this.carVin = carVin;
this.oldCarOwner = oldCarOwner;
this.newCarOwner = newCarOwner;
}
private final ProgressTracker.Step RETRIEVING_NOTARY = new ProgressTracker.Step("Retrieving Notary");
private final ProgressTracker.Step CREATE_TRANSACTION_INPUT= new ProgressTracker.Step("Creating Transaction Input");
private final ProgressTracker.Step CREATE_TRANSACTION_OUTPUT= new ProgressTracker.Step("Creating Transaction Output");
private final ProgressTracker.Step CREATE_TRANSACTION_BUILDER= new ProgressTracker.Step("Creating transaction Builder");
private final ProgressTracker.Step SIGN_TRANSACTION = new ProgressTracker.Step("Signing Transaction");
private final ProgressTracker.Step INITIATE_SESSION = new ProgressTracker.Step("Initiating session with counterparty");
private final ProgressTracker.Step FINALIZE_FLOW = new ProgressTracker.Step("Finalizing the flow");
private final ProgressTracker progressTracker = new ProgressTracker(
RETRIEVING_NOTARY,
CREATE_TRANSACTION_OUTPUT,
CREATE_TRANSACTION_BUILDER,
SIGN_TRANSACTION,
INITIATE_SESSION,
FINALIZE_FLOW
);
#Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
public StateAndRef<CarState> checkForCarStates(UUID accountId) throws FlowException {
// This returns the old owners unconsumed state
QueryCriteria.VaultQueryCriteria criteria = new QueryCriteria.VaultQueryCriteria().withExternalIds(Arrays.asList(accountId)).withStatus(Vault.StateStatus.UNCONSUMED);
//QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
//QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
List<StateAndRef<CarState>> CarStates = getServiceHub().getVaultService().queryBy(CarState.class, criteria).getStates();
boolean inputFound = false;
int t = CarStates.size();
input = 0;
for (int x = 0; x < t; x++) {
if (CarStates.get(x).getState().getData().getCarVIN().equals(carVin)) {
// if (CarStates.get(x).getState().getData().getLinearId().getExternalId().equals(linearId.getExternalId())) {
input = x;
inputFound = true;
}
}
if (inputFound) {
System.out.println("\n Input Found");
} else {
System.out.println("\n Input not found");
throw new FlowException();
}
return CarStates.get(input);
}
#Suspendable
public String call() throws FlowException {
//Retrieve the notary identity from the network map
progressTracker.setCurrentStep(RETRIEVING_NOTARY);
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
//<-------------------------------------- NEW CODE ------------------------------>
// Get the oldOwnerAccount info here
AccountInfo oldOwnerAccountInfo = UtilitiesKt.getAccountService(this).accountInfo(oldCarOwner).get(0).getState().getData();
//Get the party here
AnonymousParty oldOwnerAccount = subFlow(new RequestKeyForAccount(oldOwnerAccountInfo));
// Get the newOwnerAccount info here
AccountInfo newOwnerAccountInfo = UtilitiesKt.getAccountService(this).accountInfo(newCarOwner).get(0).getState().getData();
//Get the party here
AnonymousParty newOwnerAccount = subFlow(new RequestKeyForAccount(oldOwnerAccountInfo));
//Create transaction components both input and output for this application
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
StateAndRef<CarState> inputState = null;
inputState = checkForCarStates(oldOwnerAccountInfo.getIdentifier().getId());
System.out.println(inputState.getState().getData().getCarMake()+" "+inputState.getState().getData().getCarVIN());
//Issuer is Toyota
//Owner is AutoSmart
//AnonymousParty issuer = inputState.getState().getData().getIssuer();
PublicKey issuerKey = inputState.getState().getData().getIssuer().getOwningKey();
PublicKey newOwnerKey = newOwnerAccount.getOwningKey();
//Create transaction components both input and output for this application
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
//CarState outputState = new CarState(carMake,carModel,carYear,carMileage,carVin,issuer,carOwner);
// String carMake = inputState.getState().getData().getCarMake();
// String carModel = inputState.getState().getData().getCarModel();
// int carYear = inputState.getState().getData().getCarYear();
// double carMile = inputState.getState().getData().getCarMileAge();
// String carVIN = inputState.getState().getData().getCarVIN();
// AnonymousParty carIssuer = inputState.getState().getData().getIssuer();
// This creating a new output ?
// CarState outputState = new CarState(carMake,carModel,carYear,carMile, carVin,carIssuer,newOwnerAccount);
// System.out.println(outputState.getParticipants());
// Thi swill collect signature from Issuer, Old owner and New Owner
// check what to provide here
//List<PublicKey> requiresSigners = Arrays.asList(getOurIdentity().getOwningKey());
// List<PublicKey> requiredSigners = inputState.getState().getData().getParticipants()
// .stream().map(AbstractParty::getOwningKey)
// .collect(Collectors.toList());
// requiredSigners.add(newOwnerAccount.getOwningKey());
List<PublicKey> requiredSigners = Arrays.asList(issuerKey,oldOwnerAccount.getOwningKey(),newOwnerKey);
System.out.println(issuerKey+","+oldOwnerAccount.getOwningKey()+","+newOwnerKey);
final Command<CarContract.Transfer> txCommand = new Command<>(
new CarContract.Transfer(),
requiredSigners
);
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addInputState(inputState)
.addOutputState(inputState.getState().getData().withNewOwner(newOwnerAccount), CID)
.addCommand(txCommand);
// Create the transaction builder here and add compenents to it
progressTracker.setCurrentStep(CREATE_TRANSACTION_BUILDER);
// Sign the transaction
progressTracker.setCurrentStep(SIGN_TRANSACTION);
txBuilder.verify(getServiceHub());
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder, getOurIdentity().getOwningKey());
System.out.println("Current nodes identity: " +getOurIdentity().getName().getOrganisation());
System.out.println("Signed transaction by old owner"+": "+signedTx);
// <----------------------------------------------------------------------->
// Code stops here
List<FlowSession> sessions = new ArrayList<>();
AccountInfo issuerAccount = UtilitiesKt.getAccountService(this).accountInfo("account1").get(0).getState().getData();
sessions.add(initiateFlow(issuerAccount.getHost()));
sessions.add(initiateFlow(newOwnerAccountInfo.getHost()));
// Create session with counterparty
progressTracker.setCurrentStep(INITIATE_SESSION);
System.out.println("Owner Host name is: "+newOwnerAccountInfo.getHost().getName().getOrganisation()+" Account is: "+newOwnerAccountInfo.getName());
System.out.println("Started session with new owner");
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, sessions, Collections.singleton(getOurIdentity().getOwningKey())));
//
// final SignedTransaction fullySignedTx = subFlow(
// new CollectSignaturesFlow(signedTx, sessions, CollectSignaturesFlow.Companion.tracker()));
//
// SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
// signedTx, sessions, CollectSignaturesFlow.tracker()));
// SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
// signedTx, sessions));
System.out.println("Passed the fullySignedTx section of the code.");
//Finalizing the transaction
progressTracker.setCurrentStep(FINALIZE_FLOW);
SignedTransaction sTx = subFlow(new FinalityFlow(fullySignedTx,sessions));
System.out.println("Getting sign from initiator");
return "Transfer Completed";
}
}
State
package com.template.states;
import com.template.contracts.CarContract;
import net.corda.core.contracts.BelongsToContract;
import net.corda.core.contracts.ContractState;
import net.corda.core.contracts.LinearState;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.serialization.ConstructorForDeserialization;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
#BelongsToContract(CarContract.class)
public class CarState implements ContractState, LinearState {
private String carMake;
private String carModel;
private int carYear;
private double carMileAge;
private String carVIN;
private AnonymousParty issuer;
private AnonymousParty owner;
private UniqueIdentifier linearId;
private List<AbstractParty> participants;
public CarState(String carMake, String carModel, int carYear, double carMileAge, String carVIN, AnonymousParty issuer, AnonymousParty owner) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileAge = carMileAge;
this.carVIN = carVIN;
this.issuer = issuer;
this.owner = owner;
this.linearId = new UniqueIdentifier();
}
#ConstructorForDeserialization
public CarState(String carMake, String carModel, int carYear, double carMileAge, String carVIN, AnonymousParty issuer, AnonymousParty owner, UniqueIdentifier linearId) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileAge = carMileAge;
this.carVIN = carVIN;
this.issuer = issuer;
this.owner = owner;
this.linearId = linearId;
}
public String getCarMake() {
return carMake;
}
public String getCarModel() {
return carModel;
}
public int getCarYear() {
return carYear;
}
public double getCarMileAge() {
return carMileAge;
}
public String getCarVIN() {
return carVIN;
}
public AnonymousParty getIssuer() {
return issuer;
}
public AnonymousParty getOwner() {
return owner;
}
#NotNull
#Override
public List<AbstractParty> getParticipants() {
return Arrays.asList(issuer,owner);
}
#NotNull
#Override
public UniqueIdentifier getLinearId() {
return linearId;
}
public CarState withNewOwner(AnonymousParty newOwner){
return new CarState(carMake,carModel,carYear,carMileAge, carVIN,issuer,newOwner, linearId);
}
}
There are many things in your flow that need to be changed:
Your approach of searching for a car by the owner then looping through the list until you find the state with the VIN that you want is not good. What if that account had 100,000 cars? You're going to loop through 100,000 records until you find the one with the VIN that you want? Instead, create a custom schema for your car state so you can create a query criteria that filters by VIN. See an example of how to create a custom schema here, here, and here.
Your car state should implement LinearState instead of just ContractState. This will allow you to "update" your state. LinearState has a linearId attribute which is unique on the network level. So when you want to update a car, you consume as an input then you create the updated version as an output; the output car must have the exact same linearId, this way you can track the different version of that car as it changes its attributes values (you query the car by that linearId).
Following step #2, your car state should have 2 constructors; one that doesn't take linearId as an input parameter and inside the constructor you assign a random value to it (this constructor is used when you issue a car, the output state is created using that constructor); then you need a second constructor that takes linearId as an input parameter and is marked with the annotation #ConstructorForDeserialization; this constructor is used in transfer when creating the output car (i.e. the car with the new owner), you will pass to it the linearId of the input car; this way the output car is considered as the updated version of the input car (because they have the same linearId).
As for your question about the required signer; it's you who decides who's the required signer for each command inside the contract (which you didn't share the code of in your question). Logically speaking, when you issue a new car, only the issuer should be required; and when you transfer a car, only the current (old) owner is required to sign.
The assumption is that the initiator of the transfer flow is the current owner of the car (you don't want someone transferring your car without your consent). So in your case the initiator of the flow (i.e. the old owner) is the only required signer (if that's what your contract says); so signing the transaction locally is sufficient.
You still need to create a FlowSession with the node of the new owner; so that finality flow sends the finalized transaction/state and the responder flow (i.e. new owner) receives the finalized transaction/state.
Related
I am trying to write a test of a service method. Most of the logic performed in their is covered by tests of those methods. I am mostly trying to see if the correct repository calls are being performed.The method returns the id of the receipt/bon it just created.
I can train the mocks of the repository with the calls being performed in the service. But my test keeps failing, because the service call is unable to return an id value (long). This is because the id is generated by performing the save method followed by the flush. I cannot seem to train my mocks to do this right.
This is the only error eccuring, that it cannot perform the service call, because it returns a null value instead of an id.
How can I mock the repository correct so testing my method will not fail on not having an id?
method:
#Override
#Transactional
public long addBonAndProducts(Boninfo boninfo) {
Consignatiebon bon = new Consignatiebon(boninfo.getGebruiker(), LocalDate.now(), boninfo.getOpmbon(), boninfo.getSolden());
LocalDate dateUit = this.createCorrectDateOut(bon.getDatumIn(), bon.getSolden());
bon.setDatumUit(dateUit);
//creates correct bonnr - logic tested elswhere
String currentLetterBon = this.findCurrentLetter();
Integer numberToInsert = this.findMostRecentBoncode(currentLetterBon);
bon.setBonCode(currentLetterBon, (numberToInsert + 1));
consignatiebonRepository.save(bon);
consignatiebonRepository.flush();
for(var product:boninfo.getLijstproducten()) {
product.setConsignatiebon(bon);
if(!(product.getStatus()==Status.STOCK)) {
product.setDatumUit(dateUit);
}
//creates correct articlenr - logi tested elswhere
String currentLetter = this.findCurrentLetter();
Integer numberToInsertBon = this.findMostRecentArtikelcode(currentLetter);
product.setArtikelCode(currentLetter, (numberToInsertBon + 1));
productRepository.save(product);
productRepository.flush();
long idProduct = product.getProductId();
bon.add(product);
}
consignatiebonRepository.save(bon);
consignatiebonRepository.flush();
bon.setTotalPieces();
bon.setTotalValueBon();
// these values are correct
System.out.println("inside function pieces: " + bon.getTotalPieces());
System.out.println("inside function pieces: " + bon.getTotalPrice());
consignatiebonRepository.save(bon);
// in the test, this is outputted as null, cause the flush method doesn't get performed
System.out.println("inside function pieces: " + bon.getIdConsignatiebon());
return bon.getIdConsignatiebon();
}
test:
#ExtendWith(MockitoExtension.class)
class ProductServiceTest {
private DefaultProductService productService;
private Kleur kleur;
private Maat maat;
private Merk merk;
private Eigenschap eigenschap;
private Product product;
private Product productTwo;
private Product productThree;
private Adres adres;
private Gebruiker gebruiker;
private Consignatiebon consignatiebon;
private Consignatiebon consignatiebonToFill;
#Mock
private ProductRepository productRepository;
#Mock
private GebruikerRepository gebruikerRepository;
#Mock
private ConsignatiebonRepository consignatiebonRepository;
#Mock
private WoocommerceApi woocommerceApi;
#Mock
private Mailing mailing;
#BeforeEach
void beforeEach() {
productService = new DefaultProductService(productRepository, consignatiebonRepository, gebruikerRepository,
woocommerceApi, mailing);
kleur = new Kleur("testkleur");
maat = new Maat("testmaat");
merk = new Merk("testmerk");
eigenschap = new Eigenschap("testclustereigenschap", "testsubeigenschap");
adres = new Adres("teststraat", "testhuisnummer", "testpostcode", "testgemeente", "testland");
gebruiker = new Gebruiker("testvoornaam","testnaam", "testriziv", "testmail#hotmail.be", true,
"testtelefoonnr", adres, Gebruikersrol.TESTLEVERANCIER, "testopmerking");
consignatiebon = new Consignatiebon(gebruiker, LocalDate.of(2020, 1, 1), "testopmerking", true);
product = new Product(gebruiker, consignatiebon, eigenschap, kleur, merk, maat, Soort.DAMES,
"testbeschrijving", BigDecimal.valueOf(10), BigDecimal.valueOf(25), Status.TEKOOP, false, true);
productTwo = new Product(gebruiker, consignatiebon, eigenschap, kleur, merk, maat, Soort.DAMES,
"testbeschrijvingTwo", BigDecimal.valueOf(10), BigDecimal.valueOf(25), Status.TEKOOP, false, true);
productThree = new Product(gebruiker, consignatiebon, eigenschap, kleur, merk, maat, Soort.DAMES,
"testbeschrijvingThree", BigDecimal.valueOf(10), BigDecimal.valueOf(25), Status.TEKOOP, false, true);
}
#Test
void addBonAndProducts() {
when(consignatiebonRepository.save(Mockito.any(Consignatiebon.class))).thenAnswer(i -> i.getArguments()[0]);
when(productRepository.save(Mockito.any(Product.class))).thenAnswer(i -> i.getArguments()[0]);
// when(productService.addBonAndProducts(Mockito.any(Boninfo.class))).thenReturn(1L);
List<Product> productlist = new ArrayList<>();
productlist.add(product);
productlist.add(productTwo);
productlist.add(productThree);
Boninfo testboninfo = new Boninfo(productlist, gebruiker, "testopmerkingbon", true);
productService.addBonAndProducts(testboninfo);
verify(consignatiebonRepository, times(3)).save(consignatiebon);
verify(productRepository, times(1)).save(product);
verify(productRepository, times(1)).save(productTwo);
verify(productRepository, times(1)).save(productThree);
}
}
I am trying to transfer a state from one owner to the other back and forth. So always end up having new state created with same values. But intend to pass the state even if it is consumed to another owner. Trying to achieve this with linear state. I have pasted the transfer flow that basically should use the same car state that was issued to transfer across owners. The same consumed car state should be possible to transfer back and forth with the same state being consumed agian and again. Is this possible in Corda. From atheory perspective I am trying to transfer the car back and forth between two or more partys.
State
#ConstructorForDeserialization
public CarState(String carMake, String carModel, int carYear, double carMileAge, String carVIN, Party issuer, Party owner,UniqueIdentifier linearId) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileAge = carMileAge;
this.carVIN = carVIN;
this.issuer = issuer;
this.owner = owner;
this.linearId = linearId;
}
Contract
if(!(inputState.getLinearId().getExternalId().equals(outputState.getLinearId().getExternalId()))){
throw new IllegalArgumentException("UUID of input state and output state must be same");
}
Flow
#InitiatingFlow
#StartableByRPC
public class CarTransferFlowInitiator extends FlowLogic<String> {
// private final String carMake;
// private final String carModel;
// private final int carYear;
private final String carVin;
// private final double carMileage;
private final Party carOwner;
private final UniqueIdentifier linearId;
private int input;
public CarTransferFlowInitiator(String carVin,Party carOwner,UniqueIdentifier linearId){
this.carVin = carVin;
this.carOwner = carOwner;
this.linearId = linearId;
}
private final ProgressTracker.Step RETRIEVING_NOTARY = new ProgressTracker.Step("Retrieving Notary");
private final ProgressTracker.Step CREATE_TRANSACTION_INPUT= new ProgressTracker.Step("Creating Transaction Input");
private final ProgressTracker.Step CREATE_TRANSACTION_OUTPUT= new ProgressTracker.Step("Creating Transaction Output");
private final ProgressTracker.Step CREATE_TRANSACTION_BUILDER= new ProgressTracker.Step("Creating transaction Builder");
private final ProgressTracker.Step SIGN_TRANSACTION = new ProgressTracker.Step("Signing Transaction");
private final ProgressTracker.Step INITIATE_SESSION = new ProgressTracker.Step("Initiating session with counterparty");
private final ProgressTracker.Step FINALIZE_FLOW = new ProgressTracker.Step("Finalizing the flow");
private final ProgressTracker progressTracker = new ProgressTracker(
RETRIEVING_NOTARY,
CREATE_TRANSACTION_OUTPUT,
CREATE_TRANSACTION_BUILDER,
SIGN_TRANSACTION,
INITIATE_SESSION,
FINALIZE_FLOW
);
#Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
public StateAndRef<CarState> checkForCarStates() throws FlowException {
//QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
List<StateAndRef<CarState>> CarStates = getServiceHub().getVaultService().queryBy(CarState.class, generalCriteria).getStates();
boolean inputFound = false;
int t = CarStates.size();
input = 0;
for (int x = 0; x < t; x++) {
if (CarStates.get(x).getState().getData().getCarVIN().equals(carVin)) {
// if (CarStates.get(x).getState().getData().getLinearId().getExternalId().equals(linearId.getExternalId())) {
input = x;
inputFound = true;
}
}
if (inputFound) {
System.out.println("\n Input Found");
// System.out.println(CarStates.get(input).getState().getData().getCarMake());
// System.out.println(CarStates.get(input).getState().getData().getCarModel());
// System.out.println(CarStates.get(input).getState().getData().getCarYear());
// System.out.println(CarStates.get(input).getState().getData().getCarMileAge());
// System.out.println(CarStates.get(input).getState().getData().getCarVIN());
} else {
System.out.println("\n Input not found");
throw new FlowException();
}
return CarStates.get(input);
}
#Suspendable
public String call() throws FlowException {
//Retrieve the notary identity from the network map
progressTracker.setCurrentStep(RETRIEVING_NOTARY);
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
//Create transaction components both input and output for this application
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
StateAndRef<CarState> inputState = null;
inputState = checkForCarStates();
//Issuer is Toyota
//Owner is AutoSmart
Party issuer = inputState.getState().getData().getIssuer();
PublicKey issuerKey = issuer.getOwningKey();
//Create transaction components both input and output for this application
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
//CarState outputState = new CarState(carMake,carModel,carYear,carMileage,carVin,issuer,carOwner);
String carMake = inputState.getState().getData().getCarMake();
String carModel = inputState.getState().getData().getCarModel();
int carYear = inputState.getState().getData().getCarYear();
double carMile = inputState.getState().getData().getCarMileAge();
String carVIN = inputState.getState().getData().getCarVIN();
Party carIssuer = inputState.getState().getData().getIssuer();
UniqueIdentifier carLinearId = inputState.getState().getData().getLinearId();
System.out.println(carLinearId);
CarState outputState = new CarState(carMake,carModel,carYear,carMile, carVin,carIssuer,carOwner,carLinearId);
List<PublicKey> requiresSigners = Arrays.asList(issuerKey,getOurIdentity().getOwningKey(),outputState.getOwner().getOwningKey());
// requiresSigners.add(outputState.getIssuer().getOwningKey());
// requiresSigners.add(outputState.getOwner().getOwningKey());
final Command<CarContract.Transfer> txCommand = new Command<>(
new CarContract.Transfer(),
requiresSigners
);
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addInputState(inputState)
.addOutputState(outputState, CID)
.addCommand(txCommand);
// Create the transaction builder here and add compenents to it
progressTracker.setCurrentStep(CREATE_TRANSACTION_BUILDER);
//TransactionBuilder txB = new TransactionBuilder(notary);
// PublicKey issuerKey = getServiceHub().getMyInfo().getLegalIdentitiesAndCerts().get(0).getOwningKey();
// PublicKey ownerKey = carOwner.getOwningKey();
//List<PublicKey> requiredSigners = ImmutableList.of(issuerKey,ownerKey);
//ArrayList<PublicKey> requiredSigners = new ArrayList<PublicKey>();
//requiredSigners.add(issuerKey);
//requiredSigners.add(ownerKey);
// Command cmd = new Command(new CarContract.Register(), getOurIdentity().getOwningKey());
//txB.addOutputState(outputState, CID)
// .addCommand(cmd);
// Sign the transaction
progressTracker.setCurrentStep(SIGN_TRANSACTION);
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Create session with counterparty
progressTracker.setCurrentStep(INITIATE_SESSION);
FlowSession issuePartySession = initiateFlow((issuer));
FlowSession otherPartySession = initiateFlow(carOwner);
ArrayList<FlowSession> sessions = new ArrayList<>();
sessions.add(otherPartySession);
sessions.add(issuePartySession);
final SignedTransaction fullySignedTx = subFlow(
new CollectSignaturesFlow(signedTx, sessions, CollectSignaturesFlow.Companion.tracker()));
// SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
// signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.tracker()));
//Finalizing the transaction
progressTracker.setCurrentStep(FINALIZE_FLOW);
subFlow(new FinalityFlow(fullySignedTx,sessions));
return "Transfer Completed";
}
}
You need to create a second constructor for your linear state which accepts as input parameter a linearId; you should mark the constructor with the annotation #ConstructorForDeserialization.
Otherwise, when your flow suspends (for any reason) and it serializes your linear state, when the flow will attempt to resume and deserialize the state; it will use your current constructor which generates a random linearId, so the flow will end up with a new/different state than yours (because it has a different linearId).
But when you create that second constructor and mark it with the annotation, the flow will use it to deserialize and create an identical state.
You can use that constructor as well when you create the output state instead of using the setter like you do now.
Edit (after you added the flow code):
You cannot use a consumed state as an input; that's what's known as the double-spend problem. Imagine you had a US Dollar state and you tried to use the same dollar twice to buy things; that cannot happen. The notary will throw and exception if you try to use a consumed state as an input.
That's why when you query, you should query for UNCONSUMED instead of ALL.
Your query solution is not correct and not efficient, imagine if you had 100,000 cars; you're going to fetch all 100,000 then loop through them until you find the car with the VIN that you want? First of all you need pagination (Corda will throw an error if your result set returns more than 200 records and you're not using pagination), second of all this will probably drain your Java heap from creating those 100,000 objects and crash your CorDapp.
Instead, you should create a custom schema for your car state; then use a custom query criteria to query by the VIN number. Have a look at the IOU example how they created a custom schema for IOU state (see here and here). Then you can use the custom schema in a VaultCustomQueryCriteria (read here).
I'm currently new to the Spring Boot Java framework and I'm building a simple application. When my service starts, I want to be able to read a raw file from a URL, parse that data, and upload it into my mongodb database of atlas. So far this is what I have:
#Service
public class CoronaVirusDataService {
private List<LocationStats> allConfirmedStats = new ArrayList<>();
MongoOperations mongoOperations;
#PostConstruct // run this method as soon as the application runs
#Scheduled(cron = "* * 1 * * *") // execute this method every day
public void fetchVirusData() {
List<LocationStats> newStats = new ArrayList<>(); // to hold the stats of each state
HttpClient client = HttpClient.newHttpClient();
// creating a new http request
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(ConstantsUtil.VIRUS_CONFIRMED_DATA_URL))
.build();
// get a response by having the client send the request
try {
HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
// parse the body of the request from csv format to readable format
StringReader csvBodyReader = new StringReader(httpResponse.body());
Iterable<CSVRecord> records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(csvBodyReader);
for (CSVRecord record: records) {
// create a model with the parsed data
LocationStats stats = new LocationStats();
stats.setState(record.get("Province/State"));
stats.setCountry(record.get("Country/Region"));
// the latest day
int latestCases = Integer.parseInt(record.get(record.size() - 1));
int prevDayCases = Integer.parseInt(record.get(record.size() - 2));
stats.setLatestTotalCases(latestCases);
stats.setDiffFromPreviousDay(prevDayCases);
mongoOperations.save(LocationStats);
// add to new stats
newStats.add(stats);
}
// assign to class array -> we use this array to display the data
this.allConfirmedStats = newStats;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
So the main issue with this is the data is not saving to the mongoDB once I call mongoOperations.save(). Also, I've learned that it is bad practice to maintain some type of state in a Service. What is the best practice for this? Will inserting the data into MongoDB take care of that since we are not managing state.
Here is my model class that I want to save to mongodb
#Document(collection = "LocationStats")
public class LocationStats {
/** Location model to show corona virus statistics in each state*/
#Id
private String state;
private String country;
private int latestTotalCases;
private int diffFromPreviousDay;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public int getLatestTotalCases() {
return latestTotalCases;
}
public void setLatestTotalCases(int latestTotalCases) {
this.latestTotalCases = latestTotalCases;
}
public int getDiffFromPreviousDay() {
return diffFromPreviousDay;
}
public void setDiffFromPreviousDay(int diffFromPreviousDay) {
this.diffFromPreviousDay = diffFromPreviousDay;
}
#Override
public String toString() {
return "LocationStats{" +
"state='" + state + '\'' +
", country='" + country + '\'' +
", latestTotalCases=" + latestTotalCases +
'}';
}
}
once I have my models saved into mongoDB, I want to read from the database and get all the data from each collection and display it on the webpage. I'm thinking I'd fetch that data within the controller class and pass it to the frontend, is this good practice? here is my controller class.
#Controller
public class HomeController {
/** Controller class to generate/render the html UI */
#Autowired
CoronaVirusDataService coronaVirusDataService;
#Autowired
MongoOperations mongoOperations;
#GetMapping("/") // map this to the root template
public String home(Model model) {
List<LocationStats> allStats = coronaVirusDataService.getAllConfirmedStats();
// instead of above getter method, have a method call that fetches all data from mongoDB and return it as a List<LocationStats>
// get the total confirmed cases
int totalConfirmedCases = allStats.stream().mapToInt(LocationStats::getLatestTotalCases).sum();
int totalNewCases = allStats.stream().mapToInt(LocationStats::getDiffFromPreviousDay).sum();
// send the models to the view
model.addAttribute("locationStats", allStats);
model.addAttribute("totalReportedCases", totalConfirmedCases);
model.addAttribute("totalNewCases", totalNewCases);
return "home";
}
}
I'm getting problem with accessing variables with getter & setter on multiple classes. I looked up this one but I'm too confused.
I have 3 type users: Admin (Position No: 0 in mysql table), Manager (Position No: 1), Clerk (Position No:2).
I have SeeReportsAndFeedbacks class. I want to show all reports by selecting rows with position_no = 0 and 1 to admin and manager, 2 to clerk. It's already done with if statement.
So clerk can see only see reports that with position_no=2
manager can see only see reports that with position_no=0 and 1
admin can see only see reports that with position_no=0 and 1
Please help me. I'm stucked here for a long time. What are wrong with my getter setters?
If i set on Login_Form, and call get it shows correct in girisyap() function but if i call get in other class named SeeReportsAndFeedbacks it shows first initial value from Users () constructor instead of set value on girisyap() function on Login_Form.
tip value takes position_no from mysql db as string, new1 value is parsing (converting) string to int for if statement
screenshot
GIST
Users Class
public class Users {
private int id;
private String username;
private String fullname;
private String password;
private String phone;
private String gender;
private byte[] image;
private int position_no;
public Users () {
setPno(1); //firsst initialize
//getFullname();
}
public Users (int uid ,String uname, String fname, String upassword, String uphone, String ugender, byte[] uimage, int pno){
this.id = uid;
this.username = uname;
this.fullname = fname;
this.password = upassword;
this.phone = uphone;
this.gender =ugender;
this.image =uimage;
this.position_no = pno;
}
public Users (int pno){
setPno(pno);
}
public int getPno(){
return position_no;
}
public void setPno(int pno){
this.position_no = pno;
}}
SeeReportsAndFeedbacks class (i removed not-related funcs or some other gui things for the question.
public class SeeReportsAndFeedbacks { // extends javax.swing.JFrame
//CLIENT client = new CLIENT();
int new1 = 9999; //testing something
int PositionNoGetiren;
//sers loginf = new Users(0, null,null,null,null,null,null,new1);
public SeeReportsAndFeedbacks() {
//initComponents();
Users loginf = new Users();
PositionNoGetiren = loginf.getPno(); //gets initial value instead of set value on login_form
System.out.println("Babakingg " + PositionNoGetiren);
//int ananas = loginf.getPno();
//fillFeedbackJTable(jTable2);
}
public void fillReportJTable(){//JTable table
//loginf.setPno(2); it works if i manually set but it's useless
//System.out.println("Loginfvalue in see reports: " + loginf.getPno() + loginf.getUsername());
//new1 = loginf.getPno(); //not works shows 0
//see.getNo();
new1=PositionNoGetiren;
String selectQuery = "SELECT * FROM `users` WHERE `position_no` = ?";
if(new1==0){//admin
selectQuery = "SELECT * FROM `reports`";
}
if(new1==1){//manager
selectQuery = "SELECT * FROM `reports` WHERE `position_no` = 1";
}
if(new1==2){//clerk
selectQuery = "SELECT * FROM `reports` WHERE `position_no` = 2";
}
//}
}}
Login_Form
public class Login_Form {
int positionNoGetiren;
/**
* Creates new form Login_Form
*/
public Login_Form() {
//initComponents();
//positionNoGetiren = 9999;
}
private void girisyap() {
//I DELETED ALL DATABASE RELATED THINGS FOR QUESTION.
//String tip = rs.getString("position_no"); //DETECTS CORRECTLY POSITION NO FROM DATABASE
String tip = "IT'S rs.getString(\"position_no\")"; //for posting question
System.out.println(tip);
int new1 = Integer.parseInt(tip);
//Users loginf = new Users(new1); //welcome yazisi icin
Users loginf = new Users(); //ONLY WORKS IN THIS CLASS.
loginf.setPno(new1); //set user type for reports class BUT IT'S NOT WORKING
System.out.println("Loginf degeri login_formdaki: " + loginf.getPno());
//THIS IF IS WORKING CORRECTLY.
if(new1==0){
//Admin form = new Admin();
//form.setVisible(true);
//form.pack();
//form.setLocationRelativeTo(null);
// form.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
if(new1==1){
//Manager form = new Manager();
//form.setVisible(true);
//form.pack();
//form.setLocationRelativeTo(null);
}
if(new1==2){
//Clerk form = new Clerk();
//form.setVisible(true);
//form.pack();
//form.setLocationRelativeTo(null);
// form.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
//this.dispose();
}
private void jButton_LoginActionPerformed(java.awt.event.ActionEvent evt) {
girisyap();}
}
There is nothing wrong with your getters and setters.
This is most likely an issue with parsing the value of TIP, that could not be parsed as an INT, maybe its a float with a weird value of like 2.00000004, or simply null. Try writing a test or log the value which your query return and check if this is the value you are looking for.
I am running into this error when I load my GWT page:
error com.google.gwt.user.client.rpc.SerializationException: Type 'com.summerbreezemotel.server.DBReservation' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded.
I looked at the docs and seemed to follow all the constraints. I also have 3 other classes which do the same thing however this one seems to cause an issue. Any help would be appreciated.
the class which is failing:DBReservation
package com.summerbreezemotel.server;
import java.util.Date;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.summerbreezemotel.shared.DataReservation;
import com.summerbreezemotel.shared.SiteConstants;
public class DBReservation extends DataReservation
{
/**
*
*/
private static final long serialVersionUID = 146606067638056872L;
Date dateReserved;
String userEmail;
public DBReservation() {}
public DBReservation(DataReservation in) {
checkIn = in.checkIn;
checkOut = in.checkOut;
form_FirstName = in.form_FirstName;
form_LastName = in.form_LastName;
form_Addr1 = in.form_Addr1;
form_Addr2 = in.form_Addr2;
form_City = in.form_City;
form_State = in.form_State;
form_Zip = in.form_Zip;
form_Country = in.form_Country;
form_Phone = in.form_Phone;
form_Email = in.form_Email;
form_CardType = in.form_CardType;
form_CardMonth = in.form_CardMonth;
form_CardNumber = in.form_CardNumber;
form_CardYear = in.form_CardYear;
form_RoomType = in.form_RoomType;
form_isSmoking = in.form_isSmoking;
form_numRooms = in.form_numRooms;
ratePerNightPerRoom = in.ratePerNightPerRoom;
totalRate = in.totalRate;
}
DataReservation getData()
{
DataReservation out = new DataReservation();
out.checkIn = checkIn;
out.checkOut = checkOut;
out.form_FirstName = form_FirstName;
out.form_LastName = form_LastName;
out.form_Addr1 = form_Addr1;
out.form_Addr2 = form_Addr2;
out.form_City = form_City;
out.form_State = form_State;
out.form_Zip = form_Zip;
out.form_Country = form_Country;
out.form_Phone = form_Phone;
out.form_Email = form_Email;
out.form_CardType = form_CardType;
out.form_CardMonth = form_CardMonth;
out.form_CardNumber = form_CardNumber;
out.form_CardYear = form_CardYear;
out.form_RoomType = form_RoomType;
out.form_isSmoking = form_isSmoking;
out.form_numRooms = form_numRooms;
out.ratePerNightPerRoom = ratePerNightPerRoom;
out.totalRate = totalRate;
return out;
}
// gets item from DB and convertes it to this
void fillReservationFromEntity(Entity item)
{
userEmail = (String) item.getProperty(SiteConstants.USER_PROPERTY);
dateReserved = (Date) item.getProperty(SiteConstants.RESERVE_DATE_PROPERTY);
checkIn = (Date) item.getProperty(SiteConstants.IN_DATE_PROPERTY);
checkOut = (Date) item.getProperty(SiteConstants.OUT_DATE_PROPERTY);
form_FirstName = (String) item.getProperty(SiteConstants.FORM_FIRSTNAME_PROPERTY);
form_LastName = (String) item.getProperty(SiteConstants.FORM_LASTNAME_PROPERTY);
form_Addr1 = (String) item.getProperty(SiteConstants.FORM_ADDR1_PROPERTY);
form_Addr2 = (String) item.getProperty(SiteConstants.FORM_ADDR2_PROPERTY);
form_City = (String) item.getProperty(SiteConstants.FORM_CITY_PROPERTY);
form_State = (String) item.getProperty(SiteConstants.FORM_STATE_PROPERTY);
form_Zip = (String) item.getProperty(SiteConstants.FORM_ZIP_PROPERTY);
form_Country = (String)item.getProperty(SiteConstants.FORM_COUNTRY_PROPERTY);
form_Phone = (String) item.getProperty(SiteConstants.FORM_PHONE_PROPERTY);
form_Email = (String) item.getProperty(SiteConstants.FORM_EMAIL_PROPERTY);
form_CardType = (String) item.getProperty(SiteConstants.FORM_CARDTYPE_PROPERTY);
form_CardMonth = (String) item.getProperty(SiteConstants.FORM_CARDMONTH_PROPERTY);
form_CardNumber = (String) item.getProperty(SiteConstants.FORM_CARDNUMBER_PROPERTY);
form_CardYear = (Long) item.getProperty(SiteConstants.FORM_CARDYEAR_PROPERTY);
form_RoomType = (Long) item.getProperty(SiteConstants.FORM_ROOMTYPE_PROPERTY);
form_isSmoking = (Boolean) item.getProperty(SiteConstants.FORM_ISSMOKING_PROPERTY);
form_numRooms = (Long) item.getProperty(SiteConstants.FORM_NUMROOMS_PROPERTY);
ratePerNightPerRoom = (Double) item.getProperty(SiteConstants.RATEPERNIGHTPERROOM_PROPERTY);
totalRate = (Double) item.getProperty(SiteConstants.TOTALRATE_PROPERTY);
}
void addReservationsToEntity(Entity item)
{
item.setProperty(SiteConstants.USER_PROPERTY, userEmail);
item.setProperty(SiteConstants.RESERVE_DATE_PROPERTY, dateReserved);
item.setProperty(SiteConstants.IN_DATE_PROPERTY, checkIn);
item.setProperty(SiteConstants.OUT_DATE_PROPERTY, checkOut);
item.setProperty(SiteConstants.FORM_FIRSTNAME_PROPERTY,form_FirstName);
item.setProperty(SiteConstants.FORM_LASTNAME_PROPERTY,form_LastName);
item.setProperty(SiteConstants.FORM_ADDR1_PROPERTY,form_Addr1);
item.setProperty(SiteConstants.FORM_ADDR2_PROPERTY,form_Addr2);
item.setProperty(SiteConstants.FORM_CITY_PROPERTY,form_City);
item.setProperty(SiteConstants.FORM_STATE_PROPERTY,form_State);
item.setProperty(SiteConstants.FORM_ZIP_PROPERTY,form_Zip);
item.setProperty(SiteConstants.FORM_COUNTRY_PROPERTY,form_Country);
item.setProperty(SiteConstants.FORM_PHONE_PROPERTY,form_Phone);
item.setProperty(SiteConstants.FORM_EMAIL_PROPERTY,form_Email);
item.setProperty(SiteConstants.FORM_CARDTYPE_PROPERTY,form_CardType);
item.setProperty(SiteConstants.FORM_CARDMONTH_PROPERTY,form_CardMonth);
item.setProperty(SiteConstants.FORM_CARDNUMBER_PROPERTY,form_CardNumber);
item.setProperty(SiteConstants.FORM_CARDYEAR_PROPERTY,form_CardYear);
item.setProperty(SiteConstants.FORM_ROOMTYPE_PROPERTY,form_RoomType);
item.setProperty(SiteConstants.FORM_ISSMOKING_PROPERTY,form_isSmoking);
item.setProperty(SiteConstants.FORM_NUMROOMS_PROPERTY,form_numRooms);
item.setProperty(SiteConstants.RATEPERNIGHTPERROOM_PROPERTY,ratePerNightPerRoom);
item.setProperty(SiteConstants.TOTALRATE_PROPERTY,totalRate);
}
void store()
{
// UserService userService = UserServiceFactory.getUserService();
// User user = userService.getCurrentUser();
// if(user.getEmail() != null)
// userEmail = user.getEmail();
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
String SummerBreezeDateStoreName = new String(SiteConstants.KEY_NAME);
Key SummerBreezeDateStoreKey = KeyFactory.createKey(SiteConstants.KEY_KEY, SummerBreezeDateStoreName);
Entity reservationsEntry = new Entity(SiteConstants.DB_RESERVATIONS, SummerBreezeDateStoreKey);
addReservationsToEntity(reservationsEntry);
datastore.put(reservationsEntry);
// use this if writes get to high
// item.setProperty("testBlob",new Blob(toByte(roomResevationRequest)));
datastore.put(reservationsEntry);
}
}
my DataReservation
package com.summerbreezemotel.shared;
import java.io.Serializable;
import java.util.Date;
public class DataReservation implements Serializable
{
private static final long serialVersionUID = -7598404617383692994L;
public String form_FirstName;
public String form_LastName;
public String form_Addr1;
public String form_Addr2;
public String form_City;
public String form_State;
public String form_Zip;
public String form_Country;
public String form_Phone;
public String form_Email;
public String form_CardType;
public String form_CardMonth;
public String form_CardNumber;
public long form_CardYear;
public long form_RoomType;
public boolean form_isSmoking;
public Date checkIn;
public Date checkOut;
public long form_numRooms;
public double ratePerNightPerRoom;
public double totalRate;
public DataReservation()
{
}
}
I think this is a typical case of shooting yourself in the foot. By extending your server object with the logic from the shared class you made is somewhat easier to create a server side class including the database code, but if you by accident return the server side instead instead of calling getData() just before sending it to the client you get the server side class which canĀ“t be send to the client of course. But the compiler won't warn you for it because it syntactical correct. So I believe the answer is not in the code given, that looks good, but in between the gwt servlet and the usage of DBReservation.
You are using a file in shared folder that implements Serializable and extending it on server side. Are you sure about this logic?? It usually will be a pojo/dto/model on the client/shared side implements IsSerializable and it is sent to the server. Then servers do the manipulation and returns it back.
It should implement the Serializable interface. You don't 'inherit' the implementation by extending the class.