I am currently working on a web application which is basically a portfolio site for different vendors.
I was working on a thread which copies the details of a vendor and puts it against a new vendor, pretty straightforward.
The thread is intended to work fine but when selecting a particular Catalog object (this catalog object contains a Velocity template), the execution stops and it goes nowhere. Invoking the thread once again just hangs the whole application.
Here is my code.
public class CopySiteThread extends Thread {
public CopySiteThread(ComponentDTO componentDTO, long vendorid, int admin_id) {
/**Application specific business logic not exposed **/
}
public void run() {
/** Application based Business Logic Not Exposed **/
//Copy Catalog first
List<Catalog> catalog = catalogDAO.getCatalog(vendorid);
System.out.println(catalog);
List<Catalog> newCat = new ArrayList<Catalog>();
HashMap<String, Integer> catIdMapList = new HashMap<String, Integer>();
Iterator<Catalog> catIterator = catalog.iterator();
while (catIterator.hasNext()) {
Catalog cat = catIterator.next();
System.out.println(cat);
int catId = catalogDAO.addTemplate(admin_id, cat.getHtml(), cat.getName(), cat.getNickname(), cat.getTemplategroup(), vendor.getVendorid());
catIdMapList.put(cat.getName(), catId);
cat = null;
}
}
}
And the thread is invoked like this.
CopySiteThread thread = new CopySiteThread(componentDTO, baseVendor, admin_id);
thread.start();
After a certain number of iterations, it gets stuck on line Catalog cat = catIterator.next();
This issue is rather strange because I've developed many applications like this without any problem.
Any help appreciated.
The actual problem was in the addCatalog method in CatalogDAO
Session session = sf.openSession();
Transaction tx = null;
Integer templateID = null;
Date date = new Date();
try {
tx = session.beginTransaction();
Catalog catalog = new Catalog();
//Business Logic
templateID = (Integer) session.save(catalog);
} catch (HibernateException ex) {
if (tx != null) tx.rolback();
} finally {
session.close();
}
return templateID;
Fixed by adding a finally clause and closing all sessions.
Related
I am creating a new state in the flow and then I am trying to consume the state by using reference input. But every time I see in the result as unconsumed state, though I was providing the reference state in the transaction's input.
public SignedTransaction call() throws FlowException {
//------------------------------------------------------------------------------------------------------------
// STEP-1:
// FIRST FLOW MUST CREATE THE NEW STATE WHICH HAS NO INPUT ( THIS WILL CREATE NEW RECORD-ANCHOR WITH LINEARID )
//
//------------------------------------------------------------------------------------------------------------
// We retrieve the notary identity from the network map.
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We create the transaction components.
AnchorState outputState = new
AnchorState(ownerId,contentHash,description,classid,timestamp,expiry, getOurIdentity(), otherParty,new UniqueIdentifier());
//required signers
List<PublicKey> requiredSigners = Arrays.asList(getOurIdentity().getOwningKey(),otherParty.getOwningKey());
//send create command with required signer signatures as below
Command command = new Command<>(new AnchorStateContract.Commands.CreateRecAnchorCmd(), requiredSigners);
// We create a transaction builder and add the components.
TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(outputState, AnchorStateContract.ID)
.addCommand(command);
// Verifying the transaction.
txBuilder.verify(getServiceHub());
// Signing the transaction.
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party.
FlowSession otherPartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
SignedTransaction notraizedtransaction = subFlow(new FinalityFlow(fullySignedTx, otherPartySession));
//------------------------------------------------------------------------------------------------------------
// STEP-2:
// SINCE NOW WE HAVE A NEW UNCONSUMED RECORD-ANCHOR SO WE MUST MAKE IT CONSUMED ( BY USING THE PREVIOUS OUTPUT AS AN INPUT)
//
//------------------------------------------------------------------------------------------------------------
StateAndRef oldStateref = getServiceHub().toStateAndRef(new StateRef(notraizedtransaction.getId(),0));
Command storeCommand = new Command<>(new AnchorStateContract.Commands.ApproveRecAnchorCmd(), requiredSigners);
TransactionBuilder txBuilder2 = new TransactionBuilder(notary)
.addInputState(oldStateref)
.addOutputState(outputState, AnchorStateContract.ID)
.addCommand(storeCommand);
txBuilder2.verify(getServiceHub());
// signing
SignedTransaction signedTx2 = getServiceHub().signInitialTransaction(txBuilder2);
// Creating a session with the other party.
FlowSession otherPartySession2 = initiateFlow(otherParty);
// Finalising the transaction.
SignedTransaction fullySignedTx2 = subFlow(new CollectSignaturesFlow(
signedTx2, Arrays.asList(otherPartySession2), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
return subFlow(new FinalityFlow(fullySignedTx2, otherPartySession2));
}
In my flow initiator class I am first creating new state of a hash which I am calling as AnchorState. This state is coming from one of the participants and then it requests to the other participant to sign. afterward the signed record is stored in the ledger but its reference used as an input for a new state change, I simply want to make this state as consumed rather than unconsumed.
The responding flow class of participant B is as below
public SignedTransaction call() throws FlowException
{
//this class is used inside call function for the verification purposes before signed by this party
class SignTxFlow extends SignTransactionFlow
{
private SignTxFlow(FlowSession otherPartySession) {
super(otherPartySession);
}
#Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
ContractState output = stx.getTx().getOutputs().get(0).getData();
require.using("This must be an AnchorState transaction.", output instanceof AnchorState);
AnchorState state = (AnchorState) output;
require.using("The AnchorState's value should be more than 6 characters", state.getContentHash().length() > 6);
return null;
});
}
}
SecureHash expectedTxId = subFlow(new SignTxFlow(otherPartySession)).getId();
return subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId));
}
This flow successfully runs and returns me unique id for the transaction but I tried everything and could not found how to change the state from unconsumed to consumed?
AFTER FIX
I realized that the vaultQuery on the CordaOS by default returns unconsumed state. Which is now clear why I was not able to get the consumed state in the first place. One more issue which I found, was lack of resources in CORDA for java though I found many kotlin based answers for a transaction with "creation and consumption" in single workflow however converting them into JAVA required some efforts.
Kotlin Based answer
Some differences I observed between Java and Kotlin approach
1) When I have tried to use the same session in my second transaction which was used in the first transaction then I get this error
java.util.concurrent.ExecutionException: net.corda.core.flows.UnexpectedFlowEndException: Tried to access ended session SessionId(toLong=1984916257986245538) with empty buffer
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at net.corda.core.internal.concurrent.CordaFutureImpl.get(CordaFutureImpl.kt)
Which means we have to create new session every time for the new transaction regardless if they are in the single workflow.
2) As I understood by looking at the Kotlin solution that we don't need to add output in the transaction if we just want to make it consumed. However when I do not add an output state in the second transaction then I get the following error which means even for the consumed state I must add the same output inside the transaction. Otherwise, the following error will get erupted again.
ava.util.concurrent.ExecutionException: net.corda.core.flows.UnexpectedFlowEndException: Counter-flow errored
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at net.corda.core.internal.concurrent.CordaFutureImpl.get(CordaFutureImpl.kt)
at com.etasjil.Client.testFlow(Client.java:92)
So it is clear that unlike kotlin, in java we need to explicitly add the output state and new session if we want to create and consume a state within same workflow.
Note: Since this is a new learning curve for me therefore, if I made any mistake in the above realization then kindly correct me. This answer could be good for the new comers in Corda who wants to code in Java rather than Kotlin.
State
#BelongsToContract(AnchorStateContract.class)
public class AnchorState implements LinearState {
public String ownerId,contentHash,description,classid,timestamp,expiry;
public Party initiatorParty, otherParty;
public UniqueIdentifier linearId;
#Override
public List<AbstractParty> getParticipants() {
return Arrays.asList(initiatorParty, otherParty);
}
public AnchorState() {
}
#ConstructorForDeserialization
public AnchorState(String ownerId, String contentHash, String description, String classid, String timestamp, String expiry, Party initiatorParty, Party otherParty, UniqueIdentifier linearId) {
this.ownerId = ownerId;
this.contentHash = contentHash;
this.description = description;
this.classid = classid;
this.timestamp = timestamp;
this.expiry = expiry;
this.initiatorParty = initiatorParty;
this.otherParty = otherParty;
this.linearId = linearId;
}
...
FlowTest case
...
...
#Test
public void test1() {
Future data = a.startFlow(new Initiator("Owner1", "1234567", "Description", "c1", Instant.now().toString(), Instant.MAX.toString(), b.getInfo().getLegalIdentities().get(0).getName().toString()));
network.runNetwork();
try {
System.out.println(data.get());
}catch (Exception e){
System.out.println(e.getMessage());
}
QueryCriteria.VaultQueryCriteria criteria1 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.CONSUMED);
Vault.Page<AnchorState> results1 = a.getServices().getVaultService().queryBy(AnchorState.class, criteria1);
System.out.println("--------------------- "+ results1.getStates().size());
QueryCriteria.VaultQueryCriteria criteria2 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
Vault.Page<AnchorState> results2 = a.getServices().getVaultService().queryBy(AnchorState.class, criteria2);
System.out.println("--------------------- "+ results2.getStates().size());
QueryCriteria.VaultQueryCriteria criteria3 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.CONSUMED);
Vault.Page<AnchorState> results3 = b.getServices().getVaultService().queryBy(AnchorState.class, criteria3);
System.out.println("--------------------- "+ results3.getStates().size());
QueryCriteria.VaultQueryCriteria criteria4 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
Vault.Page<AnchorState> results4 = b.getServices().getVaultService().queryBy(AnchorState.class, criteria4);
System.out.println("--------------------- "+ results4.getStates().size());
}
I got 1,2,1,2 as the outputs which tells 1 consumed state in node a & b, totally 2 states in node a and b(1 consumed and 1 unconsumed).
I´m build a REST service through Spring and Swagger with CMIS Protocol in Maven. My services works well until the moment when I´m doing simultaneous calls through Jmeter to stress the system. Context: the service get an ID from an ID creator to generate a node in Alfresco. The id cannot be repeated in the system. this id creator take the last created node and sum +1. I tried with Thread.sleep and TimeUnit. The Jmeter Answer in 10 calls in one sec that the Node was created 10 times with the same ID.
Extract of current code: Controller:
#ApiOperation(value = "Crea un Tipo de documento")
#RequestMapping(value = "/create2", method = RequestMethod.POST)
ResponseMessage createTypeDocument2(#RequestParam String description) throws InterruptedException {
return typeDocumentService.createTypeDocument(description);
}
Service:
#Transactional
public ResponseMessage createTypeDocument(String description) throws InterruptedException {
Session session = obtieneSesion();
int id = this.idCreator2();
int aux = 0;
if(searchDocuments(id)==null) {
aux=0;
}else {
aux = Integer.parseInt(searchDocuments(id).getProperty("bc:id").getValueAsString());
}
ResponseMessage rm = new ResponseMessage();
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(500));
Thread.sleep(1000);
if(id != aux ) {
SimpleDateFormat sdf = new SimpleDateFormat("SSS");
Date date = new Date();
String nombre = sdf.format(date.getTime());
DocumentTypeDTO docFilter = new DocumentTypeDTO();
Folder root = (Folder) session.getObjectByPath("/DataList-BCH");
HashMap<String, Object> metadata = new HashMap<String, Object>();
metadata.put(PropertyIds.OBJECT_TYPE_ID, "***************");
metadata.put(PropertyIds.NAME, nombre);
metadata.put("bc:id", id);
metadata.put("bc:available", activo);
metadata.put("bc:description", description);
Document newDoc = root.createDocument(metadata, null, null);
docFilter.setId( Integer.parseInt(newDoc.getProperty("bc:id").getValueAsString()));
docFilter.setDescription(newDoc.getProperty("bc:description").getValueAsString());
docFilter.setUuid(newDoc.getId().replaceAll(";1.0", "").replace("workspace://SpacesStore/", ""));
rm.setMensaje("Exitoso");
rm.setCodigo(200);
rm.setObjeto(docFilter);
return rm;
}else {
rm.setCodigo(-1);
rm.setMensaje("La ID ya existe");
return rm;
}
}
SearchDocument method if the object is not found return null. I try multiple options without results, for that reason I need your help to resolve it. Thanks in Advice
RESOLVED IN STACKOVERFLOW IN SPANISH:
RESOLVED
public class Register {
#Autowired
private DataSource dataSource;
#Autowired
private DCNListener listener;
private OracleConnection oracleConnection = null;
private DatabaseChangeRegistration dcr = null;
private Statement statement = null;
private ResultSet rs = null;
#PostConstruct
public void init() {
this.register();
}
private void register() {
Properties props = new Properties();
props.put(OracleConnection.DCN_NOTIFY_ROWIDS, "true");
props.setProperty(OracleConnection.DCN_IGNORE_DELETEOP, "true");
props.setProperty(OracleConnection.DCN_IGNORE_UPDATEOP, "true");
try {
oracleConnection = (OracleConnection) dataSource.getConnection();
dcr = oracleConnection.registerDatabaseChangeNotification(props);
statement = oracleConnection.createStatement();
((OracleStatement) statement).setDatabaseChangeRegistration(dcr);
rs = statement.executeQuery(listenerQuery);
while (rs.next()) {
}
dcr.addListener(listener);
String[] tableNames = dcr.getTables();
Arrays.stream(tableNames)
.forEach(i -> log.debug("Table {}" + " registered.", i));
} catch (SQLException e) {
e.printStackTrace();
close();
}
}
}
My Listener:
public class DCNListener implements DatabaseChangeListener {
#Override
public void onDatabaseChangeNotification(DatabaseChangeEvent databaseChangeEvent) {
TableChangeDescription[] tableChanges = databaseChangeEvent.getTableChangeDescription();
for (TableChangeDescription tableChange : tableChanges) {
RowChangeDescription[] rcds = tableChange.getRowChangeDescription();
for (RowChangeDescription rcd : rcds) {
RowOperation op = rcd.getRowOperation();
String rowId = rcd.getRowid().stringValue();
switch (op) {
case INSERT:
//process
break;
case UPDATE:
//do nothing
break;
case DELETE:
//do nothing
break;
default:
//do nothing
}
}
}
}
}
In my Spring boot application, I have an Oracle DCN Register class that listens for INSERTS in an event table of my database. I am listening for insertion new records.
In this Event table, I have different types of events that my application supports, lets say EventA and EventB.
The application gui allows you to upload in bulk these type of events which translate into INSERT into the oracle database table I am listening to.
For one of the event types, my application is not capturing the INSERT ONLY when it is 20 or more events uploaded in bulk, but for the other event type, I do not experience this problem.
So lets say user inserts eventA any number < 20, my application captures the inserts. But if the number of eventA inserts exceeds 20, it does not capture.
This is not the case for eventB which works smoothly. I'd like to understand if I'm missing anything in term of registration and anything I can look out for maybe in the database or what the issue could be here?
You should also look for the ALL_ROWS event from:
EnumSet<TableChangeDescription.TableOperation> tableOps = tableChange.getTableOperations();
if(tableOps.contains(TableChangeDescription.TableOperation.ALL_ROWS)){
// Invalidate the cache
}
Quote fromt the JavaDoc:
The ALL_ROWS event is sent when the table is completely invalidated and row level information isn't available. If the DCN_NOTIFY_ROWIDS option hasn't been turned on during registration, then all events will have this OPERATION_ALL_ROWS flag on. It can also happen in situations where too many rows have changed and it would be too expensive for the server to send the list of them.
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/jajdb/oracle/jdbc/dcn/TableChangeDescription.TableOperation.html#ALL_ROWS
I am working on a client-server project in GWT and after the user fills a form, I would like to update my database and eventually create new datas.
For that, I create a "sensor" and the system link with which it is associated. But, the "sensor" is associated with a sensor Family too and I must create the link in my table sensorFamilySensorFamilyLink but I do not managed.
Indeed, here is my code for the creation of the sensor and the link:
#Override
public void updateSensor(SystemDTO system, SensorDTO sensorDTO, String sensorFamName) {
Sensor sensor = null;
SensorFamily sensorFam = null;
if(sensorDTO.getId() <0) {
sensor = new Sensor();
sensor.fillFromDTO(sensorDTO);
sensor.create();
}
else {
sensor = Sensor.queryById(sensorDTO.getId());
sensor.fillFromDTO(sensorDTO);
sensor.update();
}
sensorFam = SensorFamily.queryByName(sensorFamName);
// Creation of the link between sensor and sensorFamily.
SensorFamilySensorLink linksensor = null;
if (!linkFam.isEmpty()) {
linksensor = linkFam.get(0); // We take the first one because there is only one.
}
if(linksensor == null) { // creation of a sensor
//List<SensorFamilySensorLink> liste = SensorFamilySensorLink.query();
linksensor = new SensorFamilySensorLink();
linksensor.setSensorId(sensor.getId());
linksensor.setSensorFamilyId(sensorFam.getId());
linksensor.create();
}
else {
linksensor.setSensorFamilyId(sensorFam.getId());
linksensor.update();
}
Whereas the update works, the creation of the link does not work. Here is my code for the method "create":
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class SensorFamilySensorLink {
/** The id. */
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private long id;
/** The associated sensor id. */
#Persistent
private long sensorId;
/** The associated sensorFamily id. */
#Persistent
private long sensorFamilyId;
/**
* Create.
*/
public void create() {
PersistenceManager pm = PMF.getManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(this);
tx.commit();
} catch(Exception e) {
} finally {
if (tx.isActive()) {
tx.rollback(); // Error occurred so rollback the PM transaction
}
}
pm.close();
}
After debuggind, the problem comes from the generation if the IDs to use the SQL method INSERT in the table. THe generated id does not work when I am doing:
pm.makePersistent(this);
Is someone can say to me what I have forgotten because I have been searching for two days and I do not how to solve the problem? Besides, I use the same code for creating the sensor link with the system and the family link and it is working for the first one.
If you need more information, do not hesitate to ask me. Thanks in advance.
I am facing a problem with hibernate with multithreading.
I am developing a swing based application where there are some number of POJO classes. The relations among the classes: Category has a set of Protocols, Protocol has a set of Step, Step has a set of Mode. All the collections are loaded lazily with fetch = FetchType.LAZY. I am maintaining a single session for the application. After getting the list of all Category, I need to start some threads to do some operations on the category list. Here I am getting LazyInitializationException. The test code is as follows:
final List<Category> cats = protocolDao.getCategoryList();
for (int i = 0; i < 10; i++) {
new Thread("THREAD_" + i) {
public void run() {
try {
for (Category category : cats) {
Set<Protocol> protocols = category.getProtocols();
for (Protocol protocol : protocols) {
Set<Step> steps = protocol.getStep();
for (Step step : steps) {
step.getModes());
}
}
}
System.out.println(Thread.currentThread().getName()+"SUCCESS" ;
} catch (Exception e) {
System.out.println("EXCEPTION ON " + Thread.currentThread().getName());
}
};
}.start();
}
The dao method is as follows:
public List<Category> getCategoryList() throws ProtocolException {
try {
Transaction transaction = session.beginTransaction();
List list = session.createCriteria(Category.class)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.addOrder(Order.asc("categoryposition")).list();
transaction.commit();
return list;
} catch (Exception e) {
throw new ProtocolException(e);
}
}
When I try to run the above code I get the following exception for some of the threads:
SEVERE: illegal access to loading collection
org.hibernate.LazyInitializationException: illegal access to loading collection
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:363)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
at org.hibernate.collection.PersistentSet.toString(PersistentSet.java:332)
at java.lang.String.valueOf(String.java:2826)
at java.lang.StringBuilder.append(StringBuilder.java:115)
at com.mycomp.core.protocol.dao.test.TestLazyLoading$1.run(TestLazyLoading.java:76)
So some of the tasks are not completed. I cannot avoid multiple threads to work on the same category list(it works fine with single thread). Every thread requires to do its own task. The database is too big to avoid lazy loading. Can anyone help me how will I be able to work with multiple threads with the same category list?
You need to ensure that only the thread that gets your entities uses them. If you have a get ids and get by id method or similar:
final int[] catIds = protocolDao.getCategoryIds();
for (int i : catIds) {
new Thread("THREAD_" + i) {
public void run() {
Category category = protocolDao.getCategory(i);
Set<Protocol> protocols = category.getProtocols();
for (Protocol protocol : protocols) {
Set<Step> steps = protocol.getStep();
for (Step step : steps) {
step.getModes());
}
}
};
}.start();
}