Can someone tell me what is wrong with this code
I get null pointer exception and I assume because of the multiple requests
#Service
public class GernericMockingServiceImpl implements GenericMockingService {
private static final Logger LOG = LoggerFactory.getLogger(GernericMockingServiceImpl.class);
#Override
public String getJsonResponse(GenericMockingForm genericMockingForm, String requestURI) throws Exception {
LOG.info("printing requestURI : "+requestURI);
String path = new URI(requestURI).getPath();
//resolves to a folder name in src/main/resources
String folderName = path.substring(path.lastIndexOf('/') + 1);
LOG.info("printing folderName : " + folderName);
String jsonResponse = null;
StringBuilder sb = new StringBuilder();
//check if the request body has prodcut id's which means that the request is for products else check for sku's which means that the request is for price or stock
if(!Objects.isNull(genericMockingForm.getProductIds()) && !genericMockingForm.getProductIds().isEmpty()){
//currently it iterates over all the product id's/sku's in the request and appends the content of all the id's
//TODO: the content of the file is not exactly how we want it to be for multiple ids' But for the single id it just works.
// TODO: Needs to be refactored later when we handle multiple id's in request
for(String productId : genericMockingForm.getProductIds()){
jsonResponse = getJson(folderName, productId, sb);
}
}else{
for (String sku : genericMockingForm.getSkus()) {
jsonResponse = getJson(folderName, sku, sb);
}
}
LOG.info("printing jsonResponse : " + jsonResponse);
return jsonResponse;
}
private String getJson(String folderName, String id, StringBuilder sb) throws Exception {
String responseJson = null;
String filePath = "data" + File.separator + folderName + File.separator + id + ".json";
LOG.info("printing filePath : " + filePath);
LOG.info("printing id : " + id);
File f = new File(filePath);
if(f.exists()){
try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filePath)) {
LOG.info("printing inputStream : " + inputStream);
if (inputStream != null) {
responseJson = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
}
if (responseJson == null || responseJson.isEmpty()) {
LOG.info("json response is null : ");
throw new JsonNotFoundException(Constant.JSON_NOT_FOUND);
}
sb.append(responseJson);
} catch (IOException e) {
LOG.info("IO exception : ");
throw new IOException(e);
} catch (Exception e) {
LOG.info(" exception : ");
throw new Exception(e);
}
}
else{
LOG.info("file doesnt exists : " + filePath);
}
return sb.toString();
}
}
**I have 3 parallel requests accessing a folder with 3 different files and trying to read from the files
This is my stack trace**
2019-03-05 17:30:29.335 INFO 82 --- [nio-8080-exec-1] c.k.m.controller.ProductController : Received request for Mocking Controller
2019-03-05 17:30:29.335 INFO 82 --- [nio-8080-exec-3] c.k.m.controller.ProductController : Received request for Mocking Controller
2019-03-05 17:30:29.335 INFO 82 --- [nio-8080-exec-2] c.k.m.controller.ProductController : Received request for Mocking Controller
2019-03-05 17:30:29.336 INFO 82 --- [nio-8080-exec-2] c.k.m.s.impl.GernericMockingServiceImpl : printing requestURI : /mocking/api/stocks
2019-03-05 17:30:29.336 INFO 82 --- [nio-8080-exec-3] c.k.m.s.impl.GernericMockingServiceImpl : printing requestURI : /mocking/get-products
2019-03-05 17:30:29.337 INFO 82 --- [nio-8080-exec-3] c.k.m.s.impl.GernericMockingServiceImpl : printing folderName : get-products
2019-03-05 17:30:29.338 INFO 82 --- [nio-8080-exec-3] c.k.m.s.impl.GernericMockingServiceImpl : printing filePath : data/get-products/1610-17637-319.json
2019-03-05 17:30:29.338 INFO 82 --- [nio-8080-exec-3] c.k.m.s.impl.GernericMockingServiceImpl : printing id : 1610-17637-319
2019-03-05 17:30:29.338 INFO 82 --- [nio-8080-exec-3] c.k.m.s.impl.GernericMockingServiceImpl : file doesnt exists : data/get-products/1610-17637-319.json
2019-03-05 17:30:29.338 INFO 82 --- [nio-8080-exec-3] c.k.m.s.impl.GernericMockingServiceImpl : printing jsonResponse :
2019-03-05 17:30:29.336 INFO 82 --- [nio-8080-exec-1] c.k.m.s.impl.GernericMockingServiceImpl : printing requestURI : /mocking/api/prices
2019-03-05 17:30:29.338 INFO 82 --- [nio-8080-exec-1] c.k.m.s.impl.GernericMockingServiceImpl : printing folderName : prices
2019-03-05 17:30:29.337 INFO 82 --- [nio-8080-exec-2] c.k.m.s.impl.GernericMockingServiceImpl : printing folderName : stocks
2019-03-05 17:30:29.343 INFO 82 --- [nio-8080-exec-2] c.k.m.s.impl.GernericMockingServiceImpl : printing filePath : data/stocks/1610-17637.json
2019-03-05 17:30:29.343 INFO 82 --- [nio-8080-exec-2] c.k.m.s.impl.GernericMockingServiceImpl : printing id : 1610-17637
2019-03-05 17:30:29.343 INFO 82 --- [nio-8080-exec-2] c.k.m.s.impl.GernericMockingServiceImpl : file doesnt exists : data/stocks/1610-17637.json
2019-03-05 17:30:29.343 INFO 82 --- [nio-8080-exec-2] c.k.m.s.impl.GernericMockingServiceImpl : printing jsonResponse :
2019-03-05 17:30:29.354 ERROR 82 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.kfz24.mockingservice.service.impl.GernericMockingServiceImpl.getJsonResponse(GernericMockingServiceImpl.java:45) ~[classes/:na]
at com.kfz24.mockingservice.controller.GenericMockingController.processRequest(GenericMockingController.java:32) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
**at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]**
I get null pointer exception and I assume because of the multiple requests
I think your assumption is wrong.
[From comments: the null is ] this line for (String sku : genericMockingForm.getSkus()) {
That seems to indicate that genericMockingForm.getSkus() is returning null since the only other object being used there is genericMockingForm which is tested above.
You should put the same null check on that form test:
if (!Objects.isNull(genericMockingForm.getSkus())) ...
If both of them are null you then should spit out some sort of usage error.
Related
Here is my source code.
This source code has no problem.
but sometimes, I met a problem with 'Cursor state not valid' like below exception message.
20211214082151/SQL SELECT/com.dao.TransactionDetailDAO/getDataList()/Cursor state not valid.
This problem has been left unresolved for too long.
Please someone help me. Why this problem happening? and How to solve this problem.
The Point is 'sometimes'. This exception happened around once in fifty.
public Vector getDataList(int arg1, int arg2, long arg3, int arg4) {
Vector vList = new Vector();
try {
m_conn = CSDBConnection.getConnection();
String sQuery = "select DATA1,DATA2 from " + TABLE + " where ARG1= ? and ARG2=? and ARG3=? and ARG4 =? ";
m_pstmt = m_conn.prepareStatement(sQuery);
int i = 0;
m_pstmt.setInt(++i, arg1);
m_pstmt.setInt(++i, arg2);
m_pstmt.setLong(++i, arg3);
m_pstmt.setInt(++i, arg4);
m_rs = m_pstmt.executeQuery();
while (m_rs.next()) {
String[] aResult = new String[2];
aResult[0] = Integer.toString(m_rs.getInt("DATA1"));
aResult[1] = m_rs.getString("DATA2");
vList.add(aResult);
}
} catch (Exception e) {
ErrorLog.writeLog(ErrorLog.ERR_DELETE_SQL, "TransactionDetailDAO", "getDataList", e.getMessage());
} finally {
CSDBConnection.cleanup(m_rs, m_pstmt);
}
return vList;
}
detail exception logs
TimeStamp: / 2021-12-16 05:01:00
Error Message : Internal driver error. (class java.lang.NullPointerException) Error Cause : java.lang.NullPointerException
Error toString : java.sql.SQLException: Internal driver error. (class java.lang.NullPointerException)
StackTraceElement Index : 0 FileName : JDError.java LineNumber : 985 ClassName : com.ibm.as400.access.JDError MethodName : createSQLExceptionSubClass
StackTraceElement Index : 1 FileName : JDError.java LineNumber : 610 ClassName : com.ibm.as400.access.JDError MethodName : throwSQLException
StackTraceElement Index : 2 FileName : JDError.java LineNumber : 568 ClassName : com.ibm.as400.access.JDError MethodName : throwSQLException
StackTraceElement Index : 3 FileName : AS400JDBCConnectionImpl.java LineNumber : 3163 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : sendAndReceive
StackTraceElement Index : 4 FileName : AS400JDBCStatement.java LineNumber : 1805 ClassName : com.ibm.as400.access.AS400JDBCStatement MethodName : commonPrepare
StackTraceElement Index : 5 FileName : AS400JDBCPreparedStatementImpl.java LineNumber : 357 ClassName : com.ibm.as400.access.AS400JDBCPreparedStatementImpl MethodName : <init>
StackTraceElement Index : 6 FileName : AS400JDBCConnectionImpl.java LineNumber : 2313 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : prepareStatement
StackTraceElement Index : 7 FileName : AS400JDBCConnectionImpl.java LineNumber : 2084 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : prepareStatement
StackTraceElement Index : 8 FileName : AS400JDBCConnectionImpl.java LineNumber : 2079 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : prepareStatement
StackTraceElement Index : 9 FileName : Atk_MiscDao.java LineNumber : 37 ClassName : com.connector.dao.mes.Atk_MiscDao MethodName : getMiscInfo2ByTable
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TimeStamp: / 2021-12-16 07:04:56
Error Message : Communication link failure. (Disconnect request received, connection terminated.) Error Cause : com.ibm.as400.access.ConnectionDroppedException: Disconnect request received, connection terminated.
Error toString : java.sql.SQLNonTransientConnectionException: Communication link failure. (Disconnect request received, connection terminated.)
StackTraceElement Index : 0 FileName : JDError.java LineNumber : 884 ClassName : com.ibm.as400.access.JDError MethodName : createSQLExceptionSubClass
StackTraceElement Index : 1 FileName : JDError.java LineNumber : 610 ClassName : com.ibm.as400.access.JDError MethodName : throwSQLException
StackTraceElement Index : 2 FileName : JDError.java LineNumber : 568 ClassName : com.ibm.as400.access.JDError MethodName : throwSQLException
StackTraceElement Index : 3 FileName : AS400JDBCConnectionImpl.java LineNumber : 3147 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : sendAndReceive
StackTraceElement Index : 4 FileName : AS400JDBCStatement.java LineNumber : 1805 ClassName : com.ibm.as400.access.AS400JDBCStatement MethodName : commonPrepare
StackTraceElement Index : 5 FileName : AS400JDBCPreparedStatementImpl.java LineNumber : 357 ClassName : com.ibm.as400.access.AS400JDBCPreparedStatementImpl MethodName : <init>
StackTraceElement Index : 6 FileName : AS400JDBCConnectionImpl.java LineNumber : 2313 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : prepareStatement
StackTraceElement Index : 7 FileName : AS400JDBCConnectionImpl.java LineNumber : 2084 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : prepareStatement
StackTraceElement Index : 8 FileName : AS400JDBCConnectionImpl.java LineNumber : 2079 ClassName : com.ibm.as400.access.AS400JDBCConnectionImpl MethodName : prepareStatement
StackTraceElement Index : 9 FileName : Atk_MiscDao.java LineNumber : 37 ClassName : com.connector.dao.mes.Atk_MiscDao MethodName : getMiscInfo2ByTable
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
I'm using Optaplanner for a hospital bed allocation project. I'm working with spring boot, spring JPA and postgres as a DB. I've set all classes with #PlanningEntity and #PlanningSolution annotations, set
#PlanningVariable, write the constraints with drool and configure it with '.xml' file .After solving the value of planning variable, which in my cas"bed" , doesn't change!!
this is the main class:
#SpringBootApplication(scanBasePackages = { "com.asma.optaplanner.demo" })
public class OptaPlannerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(OptaPlannerDemoApplication.class, args);
}
#Bean
public CommandLineRunner demoData(PatientAdmissionScheduleHelper helper) {
return args -> {
SolverFactory<PatientAdmissionSchedule> solverfactory = SolverFactory.createFromXmlResource("Solver_Config.xml");
helper.initilizeDataBase();
System.out.println("before solving");
Solver<PatientAdmissionSchedule> solver = solverfactory.buildSolver();
PatientAdmissionSchedule unsolvedSchedule = helper.getSchedule();
unsolvedSchedule.getAdmissionList().forEach(admission -> {
System.out.println(admission.toString());
});
solver.solve(unsolvedSchedule);
PatientAdmissionSchedule solvedSchedule = solver.getBestSolution();
System.out.println("after solving");
solvedSchedule.getAdmissionList().forEach(admission -> {
System.out.println(admission.toString());
});
};
}
the result:
before solving
2020-06-03 21:33:05.256 WARN 9228 --- [ main] o.d.c.kie.builder.impl.KieBuilderImpl : File 'Constraint.drl' is in folder '' but declares package 'com.asma.optaplanner.demo'. It is advised to have a correspondance between package and folder names.
PatientName patient1bed=Bed [externalCode= bed11, room= Room [name=room1, capacity=2], indexInRoom=1], From 1, To5
PatientName patient2bed=Bed [externalCode= bed12, room= Room [name=room1, capacity=2], indexInRoom=2], From 2, To4
2020-06-03 21:33:06.208 INFO 9228 --- [ main] o.o.core.impl.solver.DefaultSolver : Solving started: time spent (77), best score (-80hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
2020-06-03 21:37:06.210 INFO 9228 --- [ main] o.o.c.i.l.DefaultLocalSearchPhase : Local Search phase (0) ended: time spent (240079), best score (0hard/0soft), score calculation speed (38961/sec), step total (418).
2020-06-03 21:37:06.222 INFO 9228 --- [ main] .c.i.c.DefaultConstructionHeuristicPhase : Construction Heuristic phase (1) ended: time spent (240091), best score (0hard/0soft), score calculation speed (1222/sec), step total (2).
2020-06-03 21:37:06.222 INFO 9228 --- [ main] o.o.core.impl.solver.DefaultSolver : Solving ended: time spent (240091), best score (0hard/0soft), score calculation speed (38946/sec), phase total (2), environment mode (REPRODUCIBLE).
after solving
2020-06-03 21:37:06.226 INFO 9228 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-06-03 21:37:06.238 ERROR 9228 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) [spring-boot-2.3.1.BUILD-SNAPSHOT.jar:2.3.1.BUILD-SNAPSHOT]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:779) [spring-boot-2.3.1.BUILD-SNAPSHOT.jar:2.3.1.BUILD-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) [spring-boot-2.3.1.BUILD-SNAPSHOT.jar:2.3.1.BUILD-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) [spring-boot-2.3.1.BUILD-SNAPSHOT.jar:2.3.1.BUILD-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.3.1.BUILD-SNAPSHOT.jar:2.3.1.BUILD-SNAPSHOT]
at com.asma.optaplanner.demo.OptaPlannerDemoApplication.main(OptaPlannerDemoApplication.java:18) [classes/:na]
Caused by: java.lang.NullPointerException: null
at com.asma.optaplanner.demo.model.Admission.toString(Admission.java:120) ~[classes/:na]
at com.asma.optaplanner.demo.OptaPlannerDemoApplication.lambda$2(OptaPlannerDemoApplication.java:40) [classes/:na]
at java.util.ArrayList.forEach(Unknown Source) ~[na:1.8.0_171]
at com.asma.optaplanner.demo.OptaPlannerDemoApplication.lambda$0(OptaPlannerDemoApplication.java:39) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) [spring-boot-2.3.1.BUILD-SNAPSHOT.jar:2.3.1.BUILD-SNAPSHOT]
... 5 common frames omitted
Constraint.drl :
package com.asma.optaplanner.demo;
//list any import classes here.
dialect "java"
import com.asma.optaplanner.demo.model.Admission ;
import com.asma.optaplanner.demo.model.AdmissionDemand ;
import com.asma.optaplanner.demo.model.Room ;
import com.asma.optaplanner.demo.model.Bed ;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;
//declare any global variables here
global HardSoftScoreHolder scoreHolder;
//Hard Constraints
//same gender at the same room in the same night
rule "SameRoomGenderconstraint"
when
$leftAdmission: Admission(
bed != null,
$room : Room,
$leftFrom : DateFromIndex,
$leftTo : DateToIndex,
$leftGender : gender)
$rightAdmission : Admission(
room == $room,
DateToIndex >= $leftFrom ,
DateFromIndex <= $leftTo ,
$rightFrom : DateFromIndex,
$rightTo : dateToIndex,
gender == $leftGender)
then
scoreHolder.addHardConstraintMatch(kcontext,
-10 * (1 + Math.max($leftTo, $rightTo) - Math.max($leftFrom, $rightFrom)));
end
rule "2PatientInTheSameBed"
//include attributes such as "salience" here...
when
$leftAdmission: Admission(
bed != null,
$bed : bed,
$leftFrom : dateFromIndex,
$leftTo : dateToIndex,
$leftId : id)
$rightAdmission: Admission(
bed == $bed,
dateToIndex >= $leftFrom ,
dateFromIndex <= $leftTo ,
$rightFrom : dateFromIndex,
$rightTo : dateToIndex,
id != $leftId)
then
scoreHolder.addHardConstraintMatch(kcontext,
-5 * (1 + Math.max($leftTo, $rightTo) - Math.max($leftFrom, $rightFrom)));
end
In case your #PlanningSolution.PlanningEntity :(#PlanningEntityCollectionProperty / #PlanningEntityProperty) don't change, you should review your drool file, it might be a bit hard to debug .drl files, u might try ConstraintProvider intreface via java, it will be easier to understand solving routine/rule.
Plus, the change is related to "ranged properties" annotated "#ValueRangeProvider", from which you can plan/optimize your solution.
As you're creating a SolverFactory in Spring Boot manually (instead of autowiring it with the optaplanner-spring-boot-starter), do pass the ClassLoader parameter to avoid common issues.
If you copied optaplanner-example's PatientAdmissionSchedule domain classes, note that it has nullable=true on the #PlanningVariable, so it can return unassigned entities. In fact, if you don't have a constraint (typically a medium constraint) to minimize that, all entities are likely to be assigned to null.
I want to create a service that combines results from two reactive sources.
One is producing Mono and another one is producing Flux. For merging I need the same value of mono for every flux emitted.
For now I have something like this
Flux.zip(
service1.getConfig(), //produces flux
service2.getContext() //produces mono
.cache().repeat()
)
This gives me what I need,
service2 is called only once
context is provided for every configuration
resulting flux has as many elements as configurations
But I have noticed that repeat() is emitting a massive amount of elements after context is cached. Is this a problem?
Is there something I can do to limit number of repeats to the number of received configurations, yet still do both request simultaneously?
Or this is not an issue and I Can safely ignore those extra emitted elements?
I tried to use combineLatest but depending on timing I some elements fo configuration can get lost and not processed.
EDIT
Looking at the suggestions from #Ricard Kollcaku I have created sample test that shows why this is not what I'm looking for.
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.test.StepVerifier;
public class SampleTest
{
Logger LOG = LoggerFactory.getLogger(SampleTest.class);
AtomicLong counter = new AtomicLong(0);
Flux<String> getFlux()
{
return Flux.fromStream(() -> {
LOG.info("flux started");
sleep(1000);
return Stream.of("a", "b", "c");
}).subscribeOn(Schedulers.parallel());
}
Mono<String> getMono()
{
return Mono.defer(() -> {
counter.incrementAndGet();
LOG.info("mono started");
sleep(1000);
return Mono.just("mono");
}).subscribeOn(Schedulers.parallel());
}
private void sleep(final long milis)
{
try
{
Thread.sleep(milis);
}
catch (final InterruptedException e)
{
e.printStackTrace();
}
}
#Test
void test0()
{
final Flux<String> result = Flux.zip(
getFlux(),
getMono().cache().repeat()
.doOnNext(n -> LOG.warn("signal on mono", n)),
(s1, s2) -> s1 + " " + s2
);
assertResults(result);
}
#Test
void test1()
{
final Flux<String> result =
getFlux().flatMap(s -> Mono.zip(Mono.just(s), getMono(),
(s1, s2) -> s1 + " " + s2));
assertResults(result);
}
#Test
void test2()
{
final Flux<String> result = getFlux().flatMap(s -> getMono().map((s1 -> s + " " + s1)));
assertResults(result);
}
void assertResults(final Flux<String> result)
{
final Flux<String> flux = result;
StepVerifier.create(flux)
.expectNext("a mono")
.expectNext("b mono")
.expectNext("c mono")
.verifyComplete();
Assertions.assertEquals(1L, counter.get());
}
Looking at the test results for test1 and test2
2020-01-20 12:55:22.542 INFO [] [] [ parallel-3] SampleTest : flux started
2020-01-20 12:55:24.547 INFO [] [] [ parallel-4] SampleTest : mono started
2020-01-20 12:55:24.547 INFO [] [] [ parallel-5] SampleTest : mono started
2020-01-20 12:55:24.548 INFO [] [] [ parallel-6] SampleTest : mono started
expected: <1> but was: <3>
I need to reject your proposal. In both cases getMono is
- invoked as many times as items in flux
- invoked after first element of flux arrives
And those are interactions that I want to avoid. My services are making http requests under the hood and they may be time consuming.
My current solution does not have this problem, but if I add logger to my zip I will get this
2020-01-20 12:55:20.505 INFO [] [] [ parallel-1] SampleTest : flux started
2020-01-20 12:55:20.508 INFO [] [] [ parallel-2] SampleTest : mono started
2020-01-20 12:55:21.523 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.528 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.529 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.529 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.529 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.529 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.530 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.530 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.530 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.530 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.531 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.531 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.531 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.531 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.531 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.532 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.532 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.532 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.532 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.533 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.534 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.534 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.534 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.534 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.534 WARN [] [] [ parallel-2] SampleTest : signal on mono
2020-01-20 12:55:21.535 WARN [] [] [ parallel-2] SampleTest : signal on mono
As you can see there is a lot of elements emitted by combining cache().repeat() together and I want to know if this is an issue and if yes then how to avoid it (but keep single invocation of mono and parallel invocation).
I think what you are trying to achieve could be done with Flux.join
Here is some example code:
Flux<Integer> flux = Flux.concat(Mono.just(1).delayElement(Duration.ofMillis(100)),
Mono.just(2).delayElement(Duration.ofMillis(500))).log();
Mono<String> mono = Mono.just("a").delayElement(Duration.ofMillis(50)).log();
List<String> list = flux.join(mono, (v1) -> Flux.never(), (v2) -> Flux.never(), (x, y) -> {
return x + y;
}).collectList().block();
System.out.println(list);
Libraries like Project Reactor and RxJava try to provide as much combinations of their capabilities as possible, but do not provide access to the instruments of combining capabilities. And as a result, there are always corner cases which are not covered.
My own DF4J, as far as I know, is the only asynchronous library which provides the means to combine capabilities. For example, this is how user can zip Flux and Mono: (of course, this class is not part of DF4J itself):
import org.df4j.core.dataflow.Actor;
import org.df4j.core.port.InpFlow;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
abstract class ZipActor<T1, T2> extends Actor {
InpFlow<T1> inpFlow = new InpFlow<>(this);
InpFlow<T2> inpScalar = new InpFlow<>(this);
ZipActor(Flux<T1> flux, Mono<T2> mono) {
flux.subscribe(inpFlow);
mono.subscribe(inpScalar);
}
#Override
protected void runAction() throws Throwable {
if (inpFlow.isCompleted()) {
stop();
return;
}
T1 element1 = inpFlow.removeAndRequest();
T2 element2 = inpScalar.current();
runAction(element1, element2);
}
protected abstract void runAction(T1 element1, T2 element2);
}
and this is how it can be used:
#Test
public void ZipActorTest() {
Flux<Integer> flux = Flux.just(1,2,3);
Mono<Integer> mono = Mono.just(5);
ZipActor<Integer, Integer> actor = new ZipActor<Integer, Integer>(flux, mono){
#Override
protected void runAction(Integer element1, Integer element2) {
System.out.println("got:"+element1+" and:"+element2);
}
};
actor.start();
actor.join();
}
The console output is as follows:
got:1 and:5
got:2 and:5
got:3 and:5
You can do it with just a simple change
getFlux()
.flatMap(s -> Mono.zip(Mono.just(s),getMono(), (s1, s2) -> s1+" "+s2))
.subscribe(System.out::println);
Flux<String> getFlux(){
return Flux.just("a","b","c");
}
Mono<String> getMono(){
return Mono.just("mono");
}
if you want to use zip but you can achieve same results using flatmap
getFlux()
.flatMap(s -> getMono()
.map((s1 -> s + " " + s1)))
.subscribe(System.out::println);
}
Flux<String> getFlux() {
return Flux.just("a", "b", "c");
}
Mono<String> getMono() {
return Mono.just("mono");
}
in both result is:
a mono
b mono
c mono
EDIT
Ok now i understand it better. Can you try this solution.
getMono().
flatMapMany(s -> getFlux().map(s1 -> s1 + " " + s))
.subscribe(System.out::println);
Flux<String> getFlux() {
return Flux.defer(() -> {
System.out.println("init flux");
return Flux.just("a", "b", "c");
});
}
Mono<String> getMono() {
return Mono.defer(() -> {
System.out.println("init Mono");
return Mono.just("sss");
});
}
I se spring boot 2.
I have a method who use a scheduler.
I try to log some info
#Scheduled(cron = "0 52 8 * * *")
private void sendEmailOrders() throws MessagingException {
List<FactoryEmailNCDto> factoryEmails = prepareDataNoncompliantSampling();
log.info("start sendEmailOrders: " + factoryEmails != null ? String.valueOf(factoryEmails.size()) : "0");
for (FactoryEmailNCDto factoryEmail : factoryEmails) {
String message = mailContentBuilder.build(factoryEmail);
if (factoryEmail.getEmails() != null && !factoryEmail.getEmails().isEmpty()) {
log.info("prepare to sent email to : " + factoryEmail.getFactoryName());
mailService.sendHtmlMail(factoryEmail.getEmails(), "Order", message);
}
}
log.info("end sendEmailOrders");
}
I get
2019-03-19 08:52:00.379 INFO 17831 --- [ scheduling-1]
com.mermacon.facade.OrdersFacade : 2 2019-03-19 08:52:00.760 INFO
17831 --- [ scheduling-1] com.mermacon.facade.OrdersFacade :
prepare to sent email to : NY 2019-03-19 08:52:03.898 ERROR 17831 ---
[ scheduling-1] o.s.s.s.TaskUtils$LoggingErrorHandler :
Unexpected e
Why I don't get this string: start sendEmailOrders in the log?
I have controler:
#GetMapping("/old")
public Product getOld() {
Product omeOld = productService.getOneOld();
log.info(String.valueOf(omeOld.getId()));
return omeOld;
}
Service:
#Override
#Transactional
public Product getOneOld() {
Product aNew = productsRepository.findTop1ByStatusOrderByCountAsc("NEW");
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return aNew;
}
And repository:
#Repository
public interface ProductsRepository extends JpaRepository<Product, Long> {
Product findTop1ByStatusOrderByCountAsc(String status);
}
I start JMeter and send 5 request in 5 threads. In result I get 5 response after 5 seconds. Each request was processed by seconds. But in log I see next:
2018-09-14 14:04:35.524 INFO 9048 --- [nio-8080-exec-1] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:04:35.525 INFO 9048 --- [nio-8080-exec-2] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:04:35.532 INFO 9048 --- [nio-8080-exec-3] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:04:35.534 INFO 9048 --- [nio-8080-exec-4] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:04:35.534 INFO 9048 --- [nio-8080-exec-6] c.e.l.demo.controller.ProductController : 1
Each thread select the same row and process it. I need that first thread select first row, second thread select second row and etc. I try use #Lock(LockModeType.PESSIMISTIC_WRITE) :
#Lock(LockModeType.PESSIMISTIC_WRITE)
Product findTop1ByStatusOrderByCountAsc(String status);
Now when I start JMeter I have next behavior:
first thread worck 5 sec, after that second thread work 5 sec and etc. 25 secons all 5 threads. And in log:
2018-09-14 14:11:40.564 INFO 13724 --- [nio-8080-exec-5] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:11:45.566 INFO 13724 --- [nio-8080-exec-4] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:11:50.567 INFO 13724 --- [nio-8080-exec-2] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:11:55.568 INFO 13724 --- [nio-8080-exec-1] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:12:00.570 INFO 13724 --- [nio-8080-exec-3] c.e.l.demo.controller.ProductController : 1
All threads select the same row(if I change this roe in first thread - it will not select in second thread if the conditions do not match).
I try this:
#Query(value = "Select * from products where status = ?1 order by count asc LIMIT 1 for update", nativeQuery = true)
Product findTop1ByStatusOrderByCountAsc(String status);
the result is the same.
But I need - first thread select first row and block it/ Second thread select next not blocked row and process. I try next:
#Query(value = "Select * from products where status = ?1 order by count asc LIMIT 1 for update of products skip locked", nativeQuery = true)
Product findTop1ByStatusOrderByCountAsc(String status);
And it work fine! :
2018-09-14 14:25:00.355 INFO 7904 --- [io-8080-exec-10] c.e.l.demo.controller.ProductController : 4
2018-09-14 14:25:00.355 INFO 7904 --- [nio-8080-exec-4] c.e.l.demo.controller.ProductController : 3
2018-09-14 14:25:00.355 INFO 7904 --- [nio-8080-exec-9] c.e.l.demo.controller.ProductController : 1
2018-09-14 14:25:00.358 INFO 7904 --- [nio-8080-exec-5] c.e.l.demo.controller.ProductController : 5
2018-09-14 14:25:00.359 INFO 7904 --- [nio-8080-exec-2] c.e.l.demo.controller.ProductController : 6
Each select in each thread select one row from non blocked rows!
But how can I repeat this with Oracle? In oracle I can not write LIMIT 1 and if I use ROWNUM = 1 each thread select same row always.