Can't get CompletableFuture and Spring REST to work together - spring

I've been trying to make a simple backend for user login.
It works fine if I don't use threads, but when I try to implement multi-threading it breaks for some reason.
For now, I'm just trying to do something as simple as retrieving all users' info from the User table in JSON format. The problem is that the Rest controller returns nothing to Postman even though dbActions.getAll() returns the correct List.
There's no error, no exception, nothing. I could really use some help. The parts of my code I'm trying to get to work are below.
Rest controller:
#Async
#RequestMapping(value="/view", method =RequestMethod.GET)
public List<User> viewAll() {
try {
List<User> list = new ArrayList<>();
list = dbActions.getAll();
return list;
}catch(Exception e) {
e.printStackTrace();
return null;
}
}
dbActions service:
public List<User> getAll() {
List<User> results = new ArrayList<>();
CompletableFuture<Void> future;
try {
future = CompletableFuture.runAsync(new Runnable() {
public void run() {
userRepo.findAll().forEach(results::add);
}
});
future.get();
return results;
} catch (Exception e) {
e.printStackTrace();
return results;
}
}

Try removing the #Async annotation from your viewAll() method

Related

How to prevent concurrent access in SpringBoot restcontroller

If while executing one rest method, at the same time came next request also so I need to prevent the second request and respond back "One person is already using". How to achieve this in spring boot? I tried the code below given. But it's not request rejected. Its added into a queue, after first request second one will execute.
#RestController
class TestController {
private static ReentrantLock lock = new ReentrantLock();
#GetMapping("tests")
public String get() {
System.err.println("----------------------------------------1 " + lock.isLocked());
if (!lock.tryLock()) {
System.err.println("--------------------------------------2 " + lock.isLocked());
return "failed";
}
try {
System.err.println("Application Locked");
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
return "success";
}

How to make resolvers run async

I am setting up java based graphQl App and find graphql-java-tools really convenient the problem though while itis pretty straight forward
With graphql-java to make field resolvers Async I couldn't Find a way to do it using graphql-java-tools
I tried
#Bean
public ExecutionStrategy executionStrategy() {
return new AsyncExecutionStrategy();
}
Here resolvers I use in order to test
#Component
public class VideoResolver implements GraphQLResolver<Video> {
public Episode getEpisode(Video video){
Episode result = new Episode();
result.setTitle("episodeTitle");
result.setUuid("EpisodeUuid");
result.setBrand("episodeBrand");
try {
Thread.sleep(2000L);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
public List<Images> getImages(Video video){
Images image = new Images();
image.setFileName("Image FileName1");
List<Images> imageList = new ArrayList<>();
imageList.add(image);
try {
Thread.sleep(2000L);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
System.out.println("Exxxxxxxxxx");
}
return imageList;
}
}
Was assuming this should run in about 2 seconds and print two different streams but no it takes 4
and print it is all in one same stream

Spring #Async with CompletableFuture

I have a doubt about this code:
#Async
public CompletableFuture<String> doFoo() {
CompletableFuture<String> fooFuture = new CompletableFuture<>();
try {
String fooResult = longOp();
fooFuture.complete(fooResult);
} catch (Exception e) {
fooFuture.completeExceptionally(e);
}
return fooFuture;
}
The question is: does doFoo return fooFuture only after longOp has finished (either correctly or exceptionally) and is therefore returning already completed futures or is Spring doing some magic and returning before executing the body? If the code is blocking on longOp(), how would you express that the computation is being fed to an executor?
Perhaps this? Any other way?
#Async
public CompletableFuture<String> doFoo() {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try {
String fooResult = longOp();
completableFuture.complete(fooResult);
} catch (Exception e) {
completableFuture.completeExceptionally(e);
}
});
return completableFuture;
}
Spring actually does all of the work behind the covers so you don't have to create the CompletableFuture yourself.
Basically, adding the #Async annotation is as if you called your original method (without the annotation) like:
CompletableFuture<User> future = CompletableFuture.runAsync(() -> doFoo());
As for your second question, in order to feed it to an executor, you can specify the exectutor bean name in the value of the #Async annotation, like so:
#Async("myExecutor")
public CompletableFuture<User> findUser(String usernameString) throws InterruptedException {
User fooResult = longOp(usernameString);
return CompletableFuture.completedFuture(fooResult);
}
The above would basically be the following as if you called your original method, like:
CompletableFuture<User> future = CompletableFuture.runAsync(() -> doFoo(), myExecutor);
And all of your exceptionally logic you would do with the returned CompletableFuture from that method.

Spring Boot CrudRepository able to read, but not write

So I've been working on an extremely simple website over the past 2 days, but I figured it would be neat to somehow link the website with a Discord bot. For the Discord bot part, I've been using the JDA library.
The issue I'm running in to is that I seem to be unable to use the save method. However, the findById and findAll seem to work perfectly fine. The way I have my code setup can be found below.
#Controller
public class IndexController extends ListenerAdapter {
private static boolean botStarted = false;
#Autowired
private NewsPostRepository newsPostRepository;
#GetMapping("/")
public String getIndex(ModelMap map) {
// TODO: add news post images.
NewsPost savedNewsPost = newsPostRepository.save(new NewsPost("Controller", "Posted through controller",
new byte[]{}, new Date(), true));
System.out.println(savedNewsPost);
return "index";
}
#GetMapping("/start")
public String startBot() {
if (!botStarted) {
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken("my-token")
.addEventListener(this)
.buildBlocking(); // Blocking vs async
} catch (LoginException e) {
e.printStackTrace();
} catch (RateLimitedException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
botStarted = true;
}
}
return "redirect:/";
}
#Override
public void onMessageReceived(MessageReceivedEvent messageReceivedEvent) {
User author = messageReceivedEvent.getAuthor();
MessageChannel channel = messageReceivedEvent.getChannel();
Message message = messageReceivedEvent.getMessage();
if (!author.isBot()) {
if (message.getContent().startsWith("!news")) {
NewsPost savedNewsPost = newsPostRepository.save(new NewsPost("Discord", "Posted through Discord",
new byte[]{}, new Date(), true));
System.out.println(savedNewsPost);
}
}
}
}
The repository:
public interface NewsPostRepository extends CrudRepository<NewsPost, String> {
}
The weird thing is, that when I go to the index page, the NewsPost saves perfectly fine, and is visible in the database.
When I try to use the Discord bot to add a NewsPost, it returns an object in the same way it would in the method for the index, with an ID that is not null and should be usable to find it in the database, however, this entry is nowhere to be found. No exception appears either. Keep in mind that both of these save() calls are identical.
I've tried to use a service and adding #Transactional but so far nothing has worked.

Transaction with binded thread connection in Spring

I want to bind a connection to a thread, and use that connection for any JdbcTemplate calls, to finally commit the changes or do a rollback.
I'm declaring all sentences from a Groovy script, so I can't control how many SQL query will be call, that's why I have to used this method and not a TransactionalTemplate or something like that. this script will call a helper class that will use that connection and JdbcTemplate, let's call that class SqlHelper.
Right now my non-working-as-needed solution is call from groovy script to that SqlHelper to initialize a transaction:
initTransaction(ecommerce)
which calls
public void initTransaction(DataSource dataSource) {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setReadOnly(false);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
// dataSourceTransactionManager.setDataSource(dataSource);
// dataSourceTransactionManager.setRollbackOnCommitFailure(true);
// dataSourceTransactionManager.setTransactionSynchronization(TransactionSynchronization.STATUS_COMMITTED);
// dataSourceTransactionManager.setDataSource(dataSource);
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
DataSourceUtils.prepareConnectionForTransaction(connection, transactionDefinition);
} catch (CannotGetJdbcConnectionException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
After that the script will call some SQL operations, like
sqlUpdate(ecommerce, insertSentence, insertParams)
which calls
public Integer update(DataSource dataSource, String sql, Map<String, Object> paramMap) {
return new NamedParameterJdbcTemplate(dataSource).update(sql, paramMap);
}
Finally I want to finish the transaction committing the changes using
commitTransaction(dataSource)
which calls
public void commitTransaction(DataSource dataSource) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.commit();
} catch (Exception e) {
rollbackTransaction(dataSource);
}
// DataSourceUtils.resetConnectionAfterTransaction(connection, TransactionDefinition.ISOLATION_DEFAULT);
// SimpleTransactionStatus transactionStatus = new SimpleTransactionStatus(false);
// try {
// dataSourceTransactionManager.commit(transactionStatus);
// jta.commit(transactionStatus);
// } catch (TransactionException e) {
// dataSourceTransactionManager.rollback(transactionStatus);
// throw new RuntimeException(e);
// }
}
private void rollbackTransaction(DataSource dataSource) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
DataSourceUtils.resetConnectionAfterTransaction(connection, TransactionDefinition.ISOLATION_DEFAULT);
}
I left commented blocks of some testing to show you what approaches I tried. I don't know very well how Spring transaction works, so I'm just trying different things and trying to learn how all this stuff works... I will provide you more information if you want ;)
Thank you in advance.
UPDATE
As M. Denium suggested, that's what I have for now:
I declared the variable, using the TransactionStatus as ThreadSafe and finally solved as:
private DataSourceTransactionManager dataSourceTransactionManager = null;
private DefaultTransactionDefinition transactionDefinition = null;
private static final ThreadLocal<TransactionStatus> transactionStatus = new ThreadLocal<TransactionStatus>() {
#Override
protected TransactionStatus initialValue() {
return null;
}
};
And then using the same call from Groovy script, using the helper methods:
public void initTransaction(DataSource dataSource) {
dataSourceTransactionManager = new DataSourceTransactionManager();
transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setReadOnly(false);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
dataSourceTransactionManager.setRollbackOnCommitFailure(true);
dataSourceTransactionManager.setTransactionSynchronization(TransactionSynchronization.STATUS_UNKNOWN);
dataSourceTransactionManager.setDataSource(dataSource);
transactionStatus.set(dataSourceTransactionManager.getTransaction(null));
}
public void commitTransaction() {
try {
LOG.info("Finishing transaction...");
dataSourceTransactionManager.commit(transactionStatus.get());
dataSourceTransactionManager.getDataSource().getConnection().close();
LOG.info("Done.");
} catch (Throwable e) {
dataSourceTransactionManager.rollback(transactionStatus.get());
throw new RuntimeException("An exception during transaction. Rolling back.");
}
}
You are trying to reimplement the things that are already implemented by the transaction abstraction of Spring. Simply use the proper PlatformTransactionManager (you can probably grab that from an ApplicationContext) keep a reference to the TransactionStatus instead of a DataSource and use that to commit/rollback.
public TransactionStatus initTransaction() {
return transactionManager.getTransaction(null);
}
public void commit(TransactionStatus status) {
transactionManager.commit(status);
}
Instead of passing the TransactionStatus around you could also store it in a ThreadLocal and retrieve it in the commit method. This would ease the pain.
Another tip you shouldn't be creating JdbcTemplates and NamedParameterJdbcTemplates those are heavy objects to create. Upon construction they consult a connection to determine which database and version this is needed for the exception conversion. So performance wise this isn't a smart thing to do. Create a single instance and reuse, the templates are thread safe so you would only be needing a single instance.
However I would strongly argue that you should actually be using Groovy and not to try to work around it. Groovy has the Sql class that can help you. You already have access to the DataSource so doing something like this would be all that is needed.
def sql = new Sql(dataSource);
sql.withTransaction {
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Minneapolis', 'Minnesota', 1867)"
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Orlando', 'Florida', 1875)"
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Gulfport', 'Mississippi', 1887)"
}
This is plain Groovy, no need to develop additional classes or to write extensive documentation to get it working. Just Groovy...

Resources