I suppose to get the response from two API and then only move forward. To achieve this tried to use completableFuture but ending up in getting NullPointerException, when fetching response from 'result' object.
Infact, completeableFuture basically not have data.
Not able to debug the thread working directly.
public APIResult execute() throws InterruptedException, ExecutionException {
CompletableFuture<TaskChair> completableFutureChair = CompletableFuture.supplyAsync(()->new TaskChair(),executorChair);
CompletableFuture<TaskBottle> completableFutureBottle = CompletableFuture.supplyAsync(()->new TaskBottle(),executorChair);
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(completableFutureChair, completableFutureBottle);
combinedFuture.get();
TaskChair taskChair = completableFutureChair.get();
TaskBottle taskBottle = completableFutureBottle.get();
List<Chair> chairs = taskChair.getChairs();
List<Bottle> bottles = taskBottle.getBottles();
APIResult result = new APIResult(chairs, bottles);
return result;
}
class TaskChair implements Callable<List<Chair>>{
List<Chair> chairs;
public List<Chair> getChairs() {
return chairs;
}
public void setChairs(List<Chair> chairs) {
this.chairs = chairs;
}
#Override
public List<Chair> call() throws Exception {
chairs = new RestAPI().getChairs();
return chairs;
}
}
public static void main(String[] args) {
RestService service = new RestService();
APIResult result = null;
try {
result = service.execute();
} catch (InterruptedException | ExecutionException e) { }
System.out.println("Chair API Status -> ");
for(Chair chair:result.getChairs()) {
System.out.println(" id : "+chair.getId()+" name : "+ chair.getName());
}
}
Related
Here is a simple spring boot application:
#SpringBootApplication
#RestController
public class ReactiveApplication {
static Flux<String> fluxString;
static volatile Queue<String> queue = new ConcurrentLinkedQueueProxy();
private static class ConcurrentLinkedQueueProxy extends ConcurrentLinkedQueue<String> {
private static final long serialVersionUID = 1L;
#Override
public boolean add(String e) {
synchronized (this) {
notify();
}
return super.add(e);
}
#Override
public String poll() {
synchronized (this) {
if(isEmpty()) {
try {
wait();
} catch (InterruptedException ex) {}
}
}
return super.peek() == null ? "" : super.poll();
}
}
static Consumer<String> consumer = str -> queue.add(str);
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(ReactiveApplication.class, args);
}
static {
for(int i = 0; i < 10; i++)
queue.add("testData " + i + " ");
}
#GetMapping(value = "/", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<String> home() {
Scheduler sch = Schedulers.newParallel("parallel-sch", 1);
List<String> list = new ArrayList<>(queue);
queue.removeAll(queue);
fluxString = Flux.<String>create(sink -> {
sink.onRequest(n -> {
for(int i = 0; i < n; i++) {
sink.next(queue.poll());
}
}).onCancel(() -> sch.dispose());
}).log().subscribeOn(sch).mergeWith(Flux.<String>fromIterable(list));
return fluxString;
}
#GetMapping("/add")
public String add( #RequestParam String s) {
consumer.accept(s);
return s;
}
}
So basically this application creates a String stream. Visiting / will grab all the string present queue and then merge anything that is added from /add resource(ignore the "Safe Methods Must be Idempotent" thing).
What I feel is strange is that when I move public static void main(...) to line 1, the application starts to misbehave and adding new values to /add doesn't have any effect. I think there must be something interesting going on that is making application misbehave. Any explaination?
I ended up using this which works great:
#SpringBootApplication
#RestController
public class ReactiveApplication {
private static BlockingQueue<String> queue = new ArrayBlockingQueue<>(1000);
private static Consumer<String> consumer = str -> {
try { queue.put(str); }
catch (InterruptedException e) {}
};
static {
for (int i = 0; i < 10; i++) queue.add("testData " + i + " ");
}
public static void main(String[] args) {
SpringApplication.run(ReactiveApplication.class, args);
}
#GetMapping(value = "/", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<String> home() {
final Scheduler sch = Schedulers.newSingle("async-flux");
return Flux.<String>generate(sink -> {
try { sink.next(queue.take()); }
catch (InterruptedException e) { }
}).log().subscribeOn(sch);
}
#GetMapping("/add")
public String add(#RequestParam String s) {
consumer.accept(s);
return s;
}
}
There is a Put method:
#RequestMapping(value = "/api/v1/fias/{fileName}", method = PUT). This method parses files. Parsing occurs in a separate thread. Therefore, for the user, the method works instantly and returns the ID of the created entity in the Mongo database.
There is a Post method:
#RequestMapping (value = "/ api / v1 / fias / interrupt / {objectId}", method = POST). This method should suspend the thread that parses the file from the POST method. Can this be implemented?
I try like this:
#Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor executorService() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("codeinside-");
executor.initialize();
return executor;
}
#RequestMapping(value = "/api/v1/fias/{fileName}", method = PUT)
public ResponseEntity<Document> update(#PathVariable(value="fileName") String fileName) throws BadParamException, NotFinishProcessException {
return ResponseEntity.status(HttpStatus.CREATED).body(fiasQueryService.updateFiasByFileName(fileName));
}
#RequestMapping(value = "/api/v1/fias/interrupt/{objectId}", method = POST)
public ResponseEntity<Document> interrupt(#PathVariable(value="objectId") String objectId) {
return ResponseEntity.status(HttpStatus.OK).body(fiasQueryService.interrupt(objectId));
}
#Service
public class FiasQueryServiceImpl implements FiasQueryService {
#Autowired
private AsyncFias asyncFias;
#Autowired
private ThreadPoolTaskExecutor executorService;
private CompletableFuture<Integer> asyncResult;
#Override
public Document updateFiasByFileName(String fileName) throws NotFinishProcessException {
String settingsPath = settingsService.getStringParam(FIAS_FILE_PATH);
File file = new File(settingsPath + "/" + fileName);
ObjectId objectId = checkAndInsertStatus(file.getName().toLowerCase());
asyncResult = asyncFias.startUpdate(file, objectId);
return new Document("_id", objectId.toString()).append("success", true);
}
#Override
public Document interrupt(String objectIdString) {
setStatus(new ObjectId(objectIdString), INTERRUPT);
asyncResult.cancel(true);
Integer cnt = null;
if (asyncResult.isCancelled()) {
ObjectId objectId = new ObjectId(objectIdString);
try {
cnt = asyncResult.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
setStatus(objectId, INTERRUPT);
return new Document("success", true).append("count", cnt);
} else {
return new Document("success", false);
}
}
}
#Service
public class AsyncFias {
#Async("threadPoolTaskExecutor")
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public CompletableFuture<Integer> startUpdate(File file, ObjectId objectId) {
// a lot of code.......
ClientSession session = mongo.startSession(ClientSessionOptions.builder().causallyConsistent(true).build());
MongoDatabase db = getDb(collectionInfo);
MongoCollection<Document> collection =
db.getCollection(collectionInfo.getCollectionName());
collection.insertMany(session, dbObjects);
session.close();
// a lot of code.......
return CompletableFuture.completedFuture(count);
}
}
But I get NPE in the line asyncResult.cancel (true);
I also tried to stop the workflow in this way: executorService.shutdown ();
But in this case, the records that should have been recorded by the time the stream stopped were rolled back. How can I stop the recording stream so that the currently recorded recordings are saved?
I changed the startUpdate method:
#Service
public class AsyncFias {
private static final Logger log = LoggerFactory.getLogger(AsyncFias.class);
private final FiasFileService fiasFileService;
private final MongoDBService mongoDBService;
private AtomicBoolean inProgress = new AtomicBoolean(false);
private AtomicInteger count = new AtomicInteger(0);
AsyncFias(FiasFileService fiasFileService, MongoDBService mongoDBService) {
this.fiasFileService = fiasFileService;
this.mongoDBService = mongoDBService;
}
public Integer getIncrement(){
return count.get();
}
#Async("threadPoolTaskExecutor")
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public Future<Void> startUpdate(File file) throws InterruptedException {
DbfUtilEnum utilEnum = DbfUtilEnum.fromFileName(file.getName().toLowerCase());
DbfMapper<Document> objMapper = utilEnum.getDbfMapper();
List<Document> dbObjects = fiasFileService.processFile(file, objMapper);
String collectionName = utilEnum.getCollectionName();
EntryMetaInfo metaInfo = new EntryMetaInfo(collectionName, collectionName, null, false, null);
List<List<Document>> lists = ListUtils.partition(dbObjects, 1000);
if (inProgress.compareAndSet(false, true)) {
for (List<Document> it : lists) {
//Thread.sleep(2000);
if (Thread.currentThread().isInterrupted()) {
System.out.println("Cancelled");
inProgress.set(false);
break;
}
mongoDBService.insertBulk(metaInfo, it);
count.getAndIncrement();
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
AsyncResult<Void> result = new AsyncResult<>(null);
result.cancel(true);
return result;
}
}
}
lists.clear();
count.set(0);
AsyncResult<Void> result = new AsyncResult<>(null);
result.cancel(true);
return result;
}
}
#Service
public class FiasQueryServiceImpl implements FiasQueryService {
private Future<Void> asyncResult;
// a lot of code
#Override
public Document updateFiasByFileName(String fileName) throws NotFinishProcessException, InterruptedException {
String settingsPath = settingsService.getStringParam(FIAS_FILE_PATH);
File file = new File(settingsPath + "/" + fileName);
ObjectId objectId = checkAndInsertStatus(file.getName().toLowerCase());
asyncResult = asyncFias.process(file);
return new Document("_id", objectId.toString()).append("success", true);
}
#Override
public Document interrupt(String objectIdString) {
asyncResult.cancel(true);
if (asyncResult.isCancelled()) {
log.info("asyncResult.isCancelled()");
ObjectId objectId = new ObjectId(objectIdString);
setStatus(objectId, INTERRUPT);
return new Document("success", true).append("count", asyncFias.getIncrement());
} else {
return new Document("success", false);
}
}
}
General conclusion: for objects of the Future type, the cancel (true) method must be called 2 times: at the time of creation of Future and at the moment of stopping the working thread.
I have a CompletableFuture<Void> that calls an asynchronous method whose return type I can't change, or anything about it.
I want to wait for this method to be complete (I manually complete it), and then return a String value, how would I do this?
public String getServer(Player p) {
FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
sendUTF(p, "GetServer");
try {
Thread.sleep(10000); //so the future doesnt complete itself
} catch (Exception e) {
e.printStackTrace();
}
}), p.getUniqueId().toString());
serverSet.add(f);
String server = "";
//server isn't final so I can't use it in the lambda
f.getFutureVoid().whenComplete(v -> server = f.getServer());
return server;
}
public class FutureServer {
private CompletableFuture<Void> futureVoid;
private String s;
private String uuid;
public FutureServer(CompletableFuture<Void> futureVoid, String uuid) {
this.futureVoid = futureVoid;
this.uuid = uuid;
}
public String getUuid() {
return uuid;
}
public CompletableFuture<Void> getFutureVoid() {
return futureVoid;
}
public boolean hasServer() {
return s != null;
}
public void setServer(String s) {
this.s = s;
}
public String getServer() {
return s;
}
}
I want to set string to equal FutureServer#getServer() (own method), but I need to wait until the CompletableFuture<Void> is completed. What do I do?
This is the method that gets called async and is unchangeable... the method I use that calls this other method asynchronously is sendUTF().
#Override
public void onPluginMessageReceived(String s, Player p, byte[] bytes) {
if (!s.equals("BungeeCord")) return;
ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
String subChannel = in.readUTF();
switch(subChannel) {
case "GetServer":
String server = in.readUTF();
serverSet.stream().filter(f -> f.getUuid().equals(p.getUniqueId().toString())).findFirst().ifPresent(f -> {
f.setServer(server); //getting the string I need and placing it into this object
f.getFutureVoid().complete(null); //completing the void future manually
});
break;
}
}
You could do this:
final AtomicReference<String> server = new AtomicReference<>("");
f.getFutureVoid().whenComplete(v -> server.set(f.getServer())).get(/* maybe add a timeout */);
return server.get();
The simplest solution is simply to join() on that future, either with:
public String getServer(Player p) {
FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
sendUTF(p, "GetServer");
try {
Thread.sleep(10000); //so the future doesnt complete itself
} catch (Exception e) {
e.printStackTrace();
}
}), p.getUniqueId().toString());
serverSet.add(f);
return f.getFutureVoid().thenApply(v -> f.getServer()).join();
}
which can easily be transformed to return a CompletableFuture<String> instead by removing the .join(), or also:
public String getServer(Player p) {
FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
sendUTF(p, "GetServer");
try {
Thread.sleep(10000); //so the future doesnt complete itself
} catch (Exception e) {
e.printStackTrace();
}
}), p.getUniqueId().toString());
serverSet.add(f);
f.getFutureVoid().join();
return f.getServer();
}
-> controller.java
public controller() {
public controller(DataInterpreter interpret,ControllerClientUtility util, InterfaceConnection inter) {
interpreter = interpret;
utility = util;
interfaced = inter;
}
}
...
public void closeOne(String vpnSessionId) throws Exception {
try{
if ( interfaced.connect() && (interfaced.CheckIntegrity(SessionId)) ){
interfaced.kill(vpnSessionId);
}else{
closeAll();
}
}catch(Exception e){
if ( e.getMessage().startsWith("INTERFACE_ERR:") ){
closeAll();
}else{
throw new Exception(e);
}
}
}
-> methods in InterfaceConnection.java
public String getReponseFor(String command) throws Exception{
if (send(command)){
return receive();
}
else{
throw new Exception("INTERFACE_ERR: Could not get Response");
}
}
public List<String> getListOfConnections() throws Exception{
String statusResponse = getReponseFor("something");
..(regex searches and then make a list connectionsConnected)
return connectionsConnected;
}
public boolean CheckIntegrity(String SessionId){
try {
List<String> connections = new ArrayList<String>();
connections = getListOfConnections();
if (connections.contains(SessionId)){
return true;
}
return false;
}catch(Exception e){
return false;
}
}
Is there a way to mock the output of getListOfConnections ? I tried doing something like this but did not work
-> controllerTest.java
#Mock private InterfaceConnection interfaced;
#Before
public void beforeTests() throws Exception {
MockitoAnnotations.initMocks(this);
impl = new Controller(interpreter,utility,interfaced);
...
#Test
public void testDisconnectOneSessionWithBadSessionId_sendCommand() throws Exception{
String badSessionId = "123:123";
List<String> mockConnections = new ArrayList<String>();
mockConnections.add("asdasds");
when(interfaced.getListOfConnections()).thenReturn(mockConnections);
impl.closeOne(badSessionId);
Mockito.verify(utility)....
}
I hope I'm clear, thanks in advance.
Run the main function in File2 , the problem is : threads stuck at "rval=MVEL.executeExpression(compiledExpression, vars);" , 10 threads run in sequential order, not parallel , I wanna know why this happened.
PS: I'm using MVEL 2.2 , the latest version
File1:MVELHelper.java
public class MVELHelper {
private static ParserContext _ctx = new ParserContext(false);
//public static Object execute(String expression, Map<String, Object> vars, Databus databus) throws Exception {
public static Object execute(String expression, Map<String, Object> vars) throws Exception {
Object rval = null;
try {
if(vars == null) {
rval = MVEL.eval(expression, new HashMap<String,Object>());
}
else {
rval = MVEL.eval(expression, vars);
}
return rval;
}
catch(Exception e) {
throw new Exception("MVEL FAILED:"+expression,e);
}
}
public static Serializable compile(String text, ParserContext ctx)
throws Exception {
if(ctx == null) {
//ctx = _ctx;
ctx=new ParserContext(false);
}
Serializable exp = null;
try {
exp = MVEL.compileExpression(text, ctx);
//exp = MVEL.compileExpression(text);
}
catch (Exception e) {
throw new Exception("failed to compile expression.", e);
}
return exp;
}
public static Object compileAndExecute(String expression, Map<String, Object> vars) throws Exception {
Object rval = null;
try {
Serializable compiledExpression=compile(expression,null);
System.out.println("[COMPILE OVER, Thread Id="+Thread.currentThread().getId()+"] ");
if(vars == null) {
rval=MVEL.executeExpression(compiledExpression, new HashMap<String,Object>());
//rval = MVEL.eval(exp, new HashMap<String,Object>());
}
else {
//rval=MVEL.executeExpression(compiledExpression, vars,(VariableResolverFactory)null);
rval=MVEL.executeExpression(compiledExpression, vars);
//rval = MVEL.eval(expression, vars);
}
return rval;
}
catch(Exception e) {
throw new Exception("MVEL FAILED:"+expression,e);
}
}
}
File2:ExecThread3.java
public class ExecThread3 implements Runnable{
Map dataMap=null;
public Map getDataMap() {
return dataMap;
}
public void setDataMap(Map dataMap) {
this.dataMap = dataMap;
}
#Override
public void run() {
Map varsMap = new HashMap();
Map dataMap=new HashMap();
dataMap.put("count",100);
varsMap.put("dataMap", dataMap);
String expression="System.out.println(\"[BEFORE Thread Id=\"+Thread.currentThread().getId()+\"] \"+dataMap.get(\"count\"));"+
"Thread.sleep(3000);"+
"System.err.println(\"[AFTER Thread Id=\"+Thread.currentThread().getId()+\"] \"+dataMap.get(\"count\"));";
try {
//MVEL.compileExpression(expression);
MVELHelper.compileAndExecute(expression, varsMap);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
for(int k=0;k<10;k++){
ExecThread3 execThread=new ExecThread3();
new Thread(execThread).start();
}
}
}