diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java index 1559cbe0017..b235a37dd60 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java @@ -22,6 +22,7 @@ import java.security.SecureRandom; import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Document @@ -29,6 +30,8 @@ public class Case implements Serializable { private static final long serialVersionUID = 3687481049847498422L; + private static final AtomicInteger counter = new AtomicInteger(0); + @Id @Getter private ObjectId _id; @@ -43,6 +46,7 @@ public class Case implements Serializable { private LocalDateTime lastModified; @Getter + @Indexed private String visualId; @NotNull @@ -276,14 +280,18 @@ private void populateDataSetBehaviorAndComponents() { private String generateVisualId() { SecureRandom random = new SecureRandom(); - int n = _id.getTimestamp() + random.nextInt(99999999); - if (this.title != null) { - n += title.length(); - } + long n = _id.getTimestamp(); + int count = counter.incrementAndGet(); + int k = random.nextInt(99999); + String timestamp = String.format("%011d", n); + String counterString = String.format("%03d", count); + String suffix = String.format("%04d", k); + String finalId = timestamp + counterString + suffix; + if (this.petriNet != null) { - return petriNet.getInitials() + "-" + n; + return petriNet.getInitials() + "-" + finalId; } - return n + ""; + return finalId; } public Object getFieldValue(String fieldId) { diff --git a/src/test/java/com/netgrif/application/engine/workflow/service/VisualIdTest.java b/src/test/java/com/netgrif/application/engine/workflow/service/VisualIdTest.java new file mode 100644 index 00000000000..f75c111c471 --- /dev/null +++ b/src/test/java/com/netgrif/application/engine/workflow/service/VisualIdTest.java @@ -0,0 +1,96 @@ +package com.netgrif.application.engine.workflow.service; + + +import com.netgrif.application.engine.petrinet.domain.PetriNet; +import com.netgrif.application.engine.petrinet.domain.VersionType; +import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.startup.SuperCreator; +import com.netgrif.application.engine.startup.SystemUserRunner; +import com.netgrif.application.engine.startup.UriRunner; +import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.repositories.TaskRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.io.FileInputStream; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +@SpringBootTest +@ActiveProfiles({"test"}) +@ExtendWith(SpringExtension.class) +public class VisualIdTest { + + @Autowired + private PetriNetRepository petriNetRepository; + + @Autowired + private TaskRepository taskRepository; + + @Autowired + private MongoTemplate mongoTemplate; + + @Autowired + private SystemUserRunner userRunner; + + @Autowired + private UriRunner uriRunner; + + @Autowired + private IPetriNetService petriNetService; + + @Autowired + private SuperCreator superCreator; + + PetriNet net; + + @BeforeEach + public void setUp() throws Exception { + mongoTemplate.getDb().drop(); + taskRepository.deleteAll(); + userRunner.run(""); + uriRunner.run(); + + petriNetService.importPetriNet(new FileInputStream("src/test/resources/prikladFM.xml"), VersionType.MAJOR, superCreator.getLoggedSuper()); + net = petriNetRepository.findAll().get(0); + assert net != null; + } + + @Test +// @RepeatedTest(100) + public void testGenerateVisualIds() throws InterruptedException { + ConcurrentHashMap ids = new ConcurrentHashMap<>(); + ExecutorService executor = Executors.newFixedThreadPool(100); + + long startTime = System.nanoTime(); + + IntStream.range(0, 1000000).forEach(i -> executor.submit(() -> { + Case caseInstance = new Case(net); + String visualId = caseInstance.getVisualId(); + ids.put(visualId, ids.getOrDefault(visualId, 0) + 1); + })); + + executor.shutdown(); + executor.awaitTermination(1, TimeUnit.MINUTES); + + long endTime = System.nanoTime(); + long duration = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime); + + long duplicates = ids.values().stream().filter(count -> count > 1).count(); + System.out.println("Total duplicates: " + duplicates); + System.out.println("Time: " + duration + " seconds"); + assert duplicates == 0 : "There should be no duplicates"; + } + +}