Processing using CompletableFuture and caching - java

With some help I was able to do asynchronous programming using Completable futures.
However, at the beginning of the application, I am creating a caching and the code is as follows:
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.*;
import org.h2.table.Plan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class test {
private static final Logger LOGGER = LoggerFactory.getLogger(test.class);
private static LoadingCache<String, Plan> something;
public static void initLoadingCache( ) {
if (something == null) {
something = CacheBuilder.newBuilder()
.concurrencyLevel(10)
.maximumSize(1000) // Maximum of 1000 records can be cached
.expireAfterWrite(60, TimeUnit.MINUTES) // Cache will expire after 60 minutes
.build(new CacheLoader<String, Plan>() { // Build the CacheLoader
#Override
public Plan load(String key) throws Exception{
Plan record = getGraphFromDB(key);
if (record == null) LOGGER.error("DB {} is not present", record);
return record;
}
});
}
}
}
getGraphFromDB is doing nothing but running a correct SQL query.
I observed in my logs I am getting record as null and hence, I threw the exception. Upon further investigation I found that I am getting No session currently bound to execution context as the Error. I added #UnitOfWork in the beginning of the method where I am performing the Async processing using Completable Future (How to perform the transformation on CompletableFuture tasks). The problem still persists and I cannot find out why it is happening.
Update based on the comment:
public Plan getGraphFromDB(String key) throws Exception{
Session session = sessionFactory.openSession();
try {
ManagedSessionContext.bind(session);
Transaction transaction = session.beginTransaction();
try {
return somequery(key);
transaction.commit();
}
catch (Exception e) {
transaction.rollback();
throw new Exception(e.getMessage());
}
} finally {
session.close();
ManagedSessionContext.unbind(sessionFactory);
}
}

Related

TaskRejectedException in Spring Boot

I have a Spring Boot application running with two endpoints running asynchronously.
Register an user in a external system using an rest API. After a successful registration put him to the DB and Redis cache.
code is something like this
#Service
public class UserRegistrationService {
#Async("asyncExecutor")
public String registerUser(DomainRequest request) throws SystemException {
try {
// External API call
extResponse = extServiceImplInterface.registerUser(extRequest);
} catch (Exception e) {
}
if (extResponse.getResCode = 0) {
// Success response from API - save to DB and redis cache
savedUser = saveUser(extResponse);
}
}
}
Refresh the each user in the DB table by calling an external rest api to each one of them. To trigger this event I call my 2nd endpoint each 5 secs, and it executes refreshUser() method.
code is something like this
#Service
public class UserRefreshService {
#Autowired
//External API call class
GetLastChatResponse getLastChatResponse;
#Async("asyncExecutor")
public void refreshUser() {
try{
//Get all registerd users from DB
List<User> currentUsers = userRepositoryInterface.findAll();
//Traverse through the list and call an external API
if(!currentUsers.isEmpty()) {
for(User item : currentUsers) {
getLastChatResponse.getLastResponse(item);
}
}
}
catch(Exception e){
}
}
}
#Service
public class GetLastChatResponse {
#Autowired
JedisPool jedisPool;
#Async("asyncExecutor")
public void getLastResponse(User item) {
//Call external rest API
LastAgentResponse lastResponseMessage = getLastAgentResponse(item);
try {
if(lastResponseMessage != null) {
//Set info to Redis cache
Jedis jedis = jedisPool.getResource();
jedis.set(item.getChatId()+Constants.LAST_INDEX, lastResponseMessage.getLastIndex());
jedis.set(item.getChatId()+Constants.LAST_TEXT_TIME, LocalDateTime.now().toString());
}
} catch (SystemException e) {
logger.error("Exception: {}", e);
}
}
}
Im using these thread pool config
#Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(200);
executor.setQueueCapacity(1);
executor.setKeepAliveSeconds(5);
executor.setThreadNamePrefix("AsyncThread-");
executor.initialize();
return executor;
}
Usually DB table contains around 10 users as the expired users are removed from the table.
The problem I'm having is I get this error when I call one of the endpoints, after running the application for sometime.
{
"code": "500",
"type": "TaskRejectedException",
"message": "Executor [java.util.concurrent.ThreadPoolExecutor#7de76256[Running, pool size = 200, active threads = 200, queued tasks = 1, completed tasks = 5089]] did not accept task: org.springframework.cloud.sleuth.instrument.async.TraceCallable#325cf639"
}
I tried changing the pool configs but it didn't work.
executor.setCorePoolSize(2000);
executor.setMaxPoolSize(4000);
executor.setQueueCapacity(1);
executor.setKeepAliveSeconds(5);
Does anyone have an idea about this?

implement multithreading while calling method to update record through API in springboot

I am writing an application in Springboot to fetch records from a database and then call an external rest api to update records into some other table. This code is completed and working as expected. As I need to improve performance as well. I am trying implement mulithreading while calling API, so that I can send multiple records at a time.
Structure :
Fetch records from a table and Store it in a list ---> Loop over list ---> multi threaded call to API
ProvRecordProcessing.java : This call will fetch records from database and create a list and call to ProvRecordService.java
ProvRecordService.java : This call will handle all API logic..
After some research, I tried to implement below to make it multithreaded :
Make ProvRecordService class to implement Runnable and override void run method
Instead of calling method, calling executorService.execute(new ProvRecordService(record));
ProvRecordProcessing.java :
I have removed other business logic from the code, only keep part where calling API method..
#Component
public class ProvRecordProcessing {
.....Code to fetch records from database....
List<UpdateProvider> provRecords = jdbcTemplate.query(sqlApiSelectQuery, new ProvRecordMapper());
//added for multithreading
ExecutorService executorService = Executors.newFixedThreadPool(2);
//looping over list records and calling API to process records
for(UpdateProvider record : provRecords) {
executorService.execute(new ProvRecordService(record));
}
executorService.shutdown();
}
}
ProvRecordService.java
Just to make it multithreaded, I have added few sections in the below code with comment : //added for multithreading
package com.emerald.paymentengineapi.service;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Service
public class ProvRecordService implements IFxiProviderService, Runnable {
#Autowired
RestSslException restSslTemplate;
#Autowired
DbConfig dbConfig;
#Autowired
UpdateProvider updateProvider; // added for multithreading
#Autowired
JdbcTemplate jdbcTemplate;
#Autowired
TokenService tokenService;
#Value("${SHIELD_API_URL}")
private String SHIELD_API_URL;
#Value("${token_expire_time}")
private String token_expire;
RestTemplate restTemplate;
DataSource dataSource;
UpdateProvider record; // added for multithreading
Logger logger = LoggerFactory.getLogger(ProvRecordService.class);
private static String FETCH_OPTIONS_SQL = "select OPTION_NAME, OPTION_VALUE from FSG.FSG_PRCB_PE_API_REQ_CONFIG";
public ProvRecordService(UpdateProvider record) { // added for multithreading
// TODO Auto-generated constructor stub
this.record = record;
}
#Override
public void run() { // added for multithreading
updateProvider(record);
}
#Scheduled(fixedRateString = "token_expire")
public ResponseEntity<String> runTokenScheduler() throws KeyManagementException, KeyStoreException, NoSuchAlgorithmException {
logger.info("Fetching Token..." + token_expire);
ResponseEntity<String> response = tokenService.getOauth2Token();
return response;
}
#Override
public ResponseEntity<String> updateProvider(UpdateProvider updateProviderRequest) {
dataSource = dbConfig.dataSource();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
try {
restTemplate = restSslTemplate.restTemplate();
} catch (KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ResponseEntity<String> response = null;
try {
if (null == TokenService.TOKEN_VALUE.get(ConfigConstants.ACCESS_TOKEN))
runTokenScheduler();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
System.out.println("value :" + TokenService.TOKEN_VALUE.get(ConfigConstants.TOKEN_TYPE));
System.out.println("access_token :" + TokenService.TOKEN_VALUE.get(ConfigConstants.ACCESS_TOKEN));
headers.add(ConfigConstants.AUTHORIZATION, TokenService.TOKEN_VALUE.get(ConfigConstants.TOKEN_TYPE) + " "
+ TokenService.TOKEN_VALUE.get(ConfigConstants.ACCESS_TOKEN));
headers.add(ConfigConstants.CLIENT_CODE, ConfigConstants.CSP_PROVIDER_BATCH);
List<RequestOptions> customers = jdbcTemplate.query(FETCH_OPTIONS_SQL,new BeanPropertyRowMapper(RequestOptions.class));
updateProviderRequest.getXpfRequestData().setRequestOptions(customers);
HttpEntity<UpdateProvider> entity = new HttpEntity<UpdateProvider>(updateProviderRequest, headers);
response = restTemplate.exchange(SHIELD_API_URL, HttpMethod.PUT, entity, String.class);
if (response.getStatusCode() == HttpStatus.NO_CONTENT) {
logger.info(updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId());
logger.info(updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
updateStatusInDB(String.valueOf(response.getStatusCodeValue()), "NO_CONTENT",
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
logger.info("Provider has been updated successfully");
} else if (response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
updateStatusInDB(String.valueOf(response.getStatusCodeValue()), "INTERNAL_SERVER_ERROR",
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
logger.error("Internal Server error occures");
} else if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
updateStatusInDB(String.valueOf(response.getStatusCodeValue()), "NOT_FOUND",
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
logger.error("Provider not found");
}
} catch (TokenServiceException ex) {
logger.error("Exception occures in calling Token API");
updateStatusInDB(ex.getMessage(), ex.getLocalizedMessage(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
//throw new RuntimeException("Exception occures in API " + ex);
} catch (HttpClientErrorException ex) {
logger.error("HttpClientErrorException occures in calling API");
updateStatusInDB(ex.getStatusText(), ex.getStatusText(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
//throw new HttpClientErrorException(ex.getStatusCode(), ex.getStatusText());
} catch (Exception ex) {
logger.error("Exception occures in calling API");
updateStatusInDB(ex.getMessage(), ex.getMessage(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
//throw new RuntimeException("Exception occures in API " + ex);
}
return response;
}
private int updateStatusInDB(String errorCode, String errorMessage, String taxId, String providerId) {
return jdbcTemplate.update(
"update FSG_WRK.FSG_PRCB_PE_API_REQUEST set ERRORCODE = ?, ERRORMESSAGE = ? where TAXID = ? and PROVIDERID= ?",
errorCode, errorMessage, taxId, providerId);
}
}
I debug this code , and it's going void run method and record is also getting populated , but after that, it's not going into the updateProvider method for processing and I am getting below error :
Exception in thread "pool-2-thread-1" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-2-thread-2" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-2-thread-3" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-2-thread-5" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Update :
After more debugging, I got to know, the issue is occurring on the below line :
dataSource = dbConfig.dataSource();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
I am trying to set dataSource here and this was working fine, when I haven't added code for multithreading. I am not able to get the reason. Please suggest.
The code is wrong here:
There is no point to create new ExecutorService per request execution.
a better approach would be creating it only once and keeping it as a data field of the ProvRecordProcessing component. Creating threads is expensive + in your approach, you don't know how many threads can be created simultaneously (what if this method is called by many users in parallel - if each creates thread pool you it can be really expensive).
In addition to the above if you use the thread pool executor you should ideally close it when the application shuts down, so don't forget to call close on predestroy or something.
Don't create a service with a new keyword, Spring won't be able to manage it and won't "process" any annotation of it (Autowired, Value, etc), so this code is wrong:
for(UpdateProvider record : provRecords) {
executorService.execute(new ProvRecordService(record));
}
Instead, Inject the service into ProvRecordProcessing Component as a singleton and call its method responsible for sending http request from runnable / callable. Here is a schematic example of what I mean:
#Component
class ProvRecordProcessing {
#Autowired
private ProvRecordService provRecordService;
....
for(UpdateProvider record : provRecords) {
executorService.execute(() -> provRecordService.updateHttpOrWhatever(record));
}
}
With this approach, ProvRecordService becomes a regular spring managed bean.
There are more advanced solutions for this, namely using #Async methods that can eliminate the need to "manually" maintain the thread pool. See This tutorial for example... Since you haven't shown those in a question, I assume it's beyond the scope of what you're asking so just keep in mind that it also exists. Of course, if you implement your code right it will do just fine.

CompletableFuture for child requests

I am trying to understand CompletableFuture in Java 8. As a part of it, I am trying to make some REST calls to solidify my understanding. I am using this library to make REST calls: https://github.com/AsyncHttpClient/async-http-client.
Please note, this library returns a Response object for the GET call.
Following is what I am trying to do:
Call this URL which gives the list of users: https://jsonplaceholder.typicode.com/users
Convert the Response to List of User Objects using GSON.
Iterate over each User object in the list, get the userID and then get the list of Posts made by the user from the following URL: https://jsonplaceholder.typicode.com/posts?userId=1
Convert each post response to Post Object using GSON.
Build a Collection of UserPost objects, each of which has a User Object and a list of posts made by the user.
public class UserPosts {
private final User user;
private final List<Post> posts;
public UserPosts(User user, List<Post> posts) {
this.user = user;
this.posts = posts;
}
#Override
public String toString() {
return "user = " + this.user + " \n" + "post = " + posts+ " \n \n";
}
}
I currently have it implemented as follows:
package com.CompletableFuture;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.asynchttpclient.Response;
import com.http.HttpResponse;
import com.http.HttpUtil;
import com.model.Post;
import com.model.User;
import com.model.UserPosts;
/**
* Created by vm on 8/20/18.
*/
class UserPostResponse {
private final User user;
private final Future<Response> postResponse;
UserPostResponse(User user, Future<Response> postResponse) {
this.user = user;
this.postResponse = postResponse;
}
public User getUser() {
return user;
}
public Future<Response> getPostResponse() {
return postResponse;
}
}
public class HttpCompletableFuture extends HttpResponse {
private Function<Future<Response>, List<User>> userResponseToObject = user -> {
try {
return super.convertResponseToUser(Optional.of(user.get().getResponseBody())).get();
} catch (Exception e) {
e.printStackTrace();
return null;
}
};
private Function<Future<Response>, List<Post>> postResponseToObject = post -> {
try {
return super.convertResponseToPost(Optional.of(post.get().getResponseBody())).get();
} catch (Exception e) {
e.printStackTrace();
return null;
}
};
private Function<UserPostResponse, UserPosts> buildUserPosts = (userPostResponse) -> {
try {
return new UserPosts(userPostResponse.getUser(), postResponseToObject.apply(userPostResponse.getPostResponse()));
} catch (Exception e) {
e.printStackTrace();
return null;
}
};
private Function<User, UserPostResponse> getPostResponseForUser = user -> {
Future<Response> resp = super.getPostsForUser(user.getId());
return new UserPostResponse(user, resp);
};
public HttpCompletableFuture() {
super(HttpUtil.getInstance());
}
public List<UserPosts> getUserPosts() {
try {
CompletableFuture<List<UserPosts>> usersFuture = CompletableFuture
.supplyAsync(() -> super.getUsers())
.thenApply(userResponseToObject)
.thenApply((List<User> users)-> users.stream().map(getPostResponseForUser).collect(Collectors.toList()))
.thenApply((List<UserPostResponse> userPostResponses ) -> userPostResponses.stream().map(buildUserPosts).collect(Collectors.toList()));
List<UserPosts> users = usersFuture.get();
System.out.println(users);
return users;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
However, I am not sure if the way I am doing this is right. More specifically, in userResponseToObject and postResponseToObject Functions, I am calling the get() method on the Future, which will be blocking.
Is there a better way to implement this?
If you plan to use CompletableFuture, you should use the ListenableFuture from async-http-client library. ListenableFuture can be converted to CompletableFuture.
The advantage of using CompletableFuture is that you can write logic that deals with Response object without having to know anything about futures or threads. Suppose you wrote the following 4 methods. 2 to make requests and 2 to parse responses:
ListenableFuture<Response> requestUsers() {
}
ListenableFuture<Response> requestPosts(User u) {
}
List<User> parseUsers(Response r) {
}
List<UserPost> parseUserPosts(Response r, User u) {
}
Now we can write a non-blocking method that retrieves posts for a given user:
CompletableFuture<List<UserPost>> userPosts(User u) {
return requestPosts(u)
.toCompletableFuture()
.thenApply(r -> parseUserPosts(r, u));
}
and a blocking method to read all posts for all users:
List<UserPost> getAllPosts() {
// issue all requests
List<CompletableFuture<List<UserPost>>> postFutures = requestUsers()
.toCompletableFuture()
.thenApply(userRequest -> parseUsers(userRequest)
.stream()
.map(this::userPosts)
.collect(toList())
).join();
// collect the results
return postFutures.stream()
.map(CompletableFuture::join)
.flatMap(List::stream)
.collect(toList());
}
Depending on the policy you want to use to manage blocking response, you can explore at least these implementations:
1) Invoking the overloaded method get of the class CompletableFuture with a timeout:
List<UserPosts> users = usersFuture.get(long timeout, TimeUnit unit);
From the documentation:
Waits if necessary for at most the given time for this future to
complete, and then returns its result, if available.
2) Using the alternative method getNow:
List users = usersFuture.getNow(T valueIfAbsent);
Returns the result value (or throws any encountered exception) if
completed, else returns the given valueIfAbsent.
3) Using CompletableFuture instead of Future, you can force manually the unlocking of get calling complete :
usersFuture.complete("Manual CompletableFuture's Result")

Access the session variable within a ServletContextListener

I am implementing an upload feature using Grails where basically a user gets to upload a text file and then the system will persist each line of that text file as a database record. While the uploading works fine, larger files take time to process and therefore they ask to have a progress bar so that users can determine if their upload is still processing or an actual error has occurred.
To do this, what I did is to create two URLs:
/upload which is the actual URL that receives the uploaded text file.
/upload/status?uploadToken= which returns the status of a certain upload based on its uploadToken.
What I did is after processing each line, the service will update a session-level counter variable:
// import ...
class UploadService {
Map upload(CommonsMultipartFile record, GrailsParameterMap params) {
Map response = [success: true]
try {
File file = new File(record.getOriginalFilename())
FileUtils.writeByteArrayToFile(file, record.getBytes())
HttpSession session = WebUtils.retrieveGrailsWebRequest().session
List<String> lines = FileUtils.readLines(file, "UTF-8"), errors = []
String uploadToken = params.uploadToken
session.status.put(uploadToken,
[message: "Checking content of the file of errors.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... regex checking per line and appending any error to the errors List
session.status.get(uploadToken).done++
}
if(errors.size() == 0) {
session.status.put(uploadToken,
[message: "Persisting record to the database.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... Performs GORM manipulation here
session.status.get(uploadToken).done++
}
}
else {
response.success = false
}
}
catch(Exception e) {
response.success = false
}
response << [errors: errors]
return response
}
}
Then create a simple WebSocket implementation that connects to the /upload/status?uploadToken= URL. The problem is that I cannot access the session variable on POGOs. I even change that POGO into a Grails service because I thought that is the cause of the issue, but I still can't access the session variable.
// import ...
#ServerEndpoint("/upload/status")
#WebListener
class UploadEndpointService implements ServletContextListener {
#OnOpen
public void onOpen(Session userSession) { /* ... */ }
#OnClose
public void onClose(Session userSession, CloseReason closeReason) { /* ... */ }
#OnError
public void onError(Throwable t) { /* ... */ }
#OnMessage
public void onMessage(String token, Session userSession) {
// Both of these cause IllegalStateException
def session = WebUtils.retrieveGrailsWebRequest().session
def session = RequestContextHolder.currentRequestAttributes().getSession()
// This returns the session id but I don't know what to do with that information.
String sessionId = userSession.getHttpSessionId()
// Sends the upload status through this line
sendMessage((session.get(token) as JSON).toString(), userSession)
}
private void sendMessage(String message, Session userSession = null) {
Iterator<Session> iterator = users.iterator()
while(iterator.hasNext()) {
iterator.next().basicRemote.sendText(message)
}
}
}
And instead, gives me an error:
Caused by IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the current request.
I already verified that the web socket is working by making it send a static String content. But what I want is to be able to get that counter and set it as the send message. I'm using Grails 2.4.4 and the Grails Spring Websocket plugin, while looks promising, is only available from Grails 3 onwards. Is there any way to achieve this, or if not, what approach should I use?
Much thanks to the answer to this question that helped me greatly solving my problem.
I just modified my UploadEndpointService the same as the one on that answer and instead of making it as a service class, I reverted it back into a POGO. I also configured it's #Serverendpoint annotation and added a configurator value. I also added a second parameter to the onOpen() method. Here is the edited class:
import grails.converters.JSON
import grails.util.Environment
import javax.servlet.annotation.WebListener
import javax.servlet.http.HttpSession
import javax.servlet.ServletContext
import javax.servlet.ServletContextEvent
import javax.servlet.ServletContextListener
import javax.websocket.CloseReason
import javax.websocket.EndpointConfig
import javax.websocket.OnClose
import javax.websocket.OnError
import javax.websocket.OnMessage
import javax.websocket.OnOpen
import javax.websocket.server.ServerContainer
import javax.websocket.server.ServerEndpoint
import javax.websocket.Session
import org.apache.log4j.Logger
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.web.json.JSONObject
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
#ServerEndpoint(value="/ep/maintenance/attendance-monitoring/upload/status", configurator=GetHttpSessionConfigurator.class)
#WebListener
class UploadEndpoint implements ServletContextListener {
private static final Logger log = Logger.getLogger(UploadEndpoint.class)
private Session wsSession
private HttpSession httpSession
#Override
void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.servletContext
ServerContainer serverContainer = servletContext.getAttribute("javax.websocket.server.ServerContainer")
try {
if (Environment.current == Environment.DEVELOPMENT) {
serverContainer.addEndpoint(UploadEndpoint)
}
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsApplication grailsApplication = ctx.grailsApplication
serverContainer.defaultMaxSessionIdleTimeout = grailsApplication.config.servlet.defaultMaxSessionIdleTimeout ?: 0
} catch (IOException e) {
log.error(e.message, e)
}
}
#Override
void contextDestroyed(ServletContextEvent servletContextEvent) {
}
#OnOpen
public void onOpen(Session userSession, EndpointConfig config) {
this.wsSession = userSession
this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName())
}
#OnMessage
public void onMessage(String message, Session userSession) {
try {
Map params = new JSONObject(message)
if(httpSession.status == null) {
params = [message: "Initializing file upload.",
size: 0,
token: 0]
sendMessage((params as JSON).toString())
}
else {
sendMessage((httpSession.status.get(params.token) as JSON).toString())
}
}
catch(IllegalStateException e) {
}
}
#OnClose
public void onClose(Session userSession, CloseReason closeReason) {
try {
userSession.close()
}
catch(IllegalStateException e) {
}
}
#OnError
public void onError(Throwable t) {
log.error(t.message, t)
}
private void sendMessage(String message, Session userSession=null) {
wsSession.basicRemote.sendText(message)
}
}
The real magic happens within the onOpen() method. There is where the accessing of the session variable takes place.

How can use JPA with play 2.5 without creating a entitymanager factory for each of my different calls?

I'm working on a API with play 2.5 for an Oracle database. Now I'm trying to use the build in features for JPA in play. On this moment I get the error
"No EntityManager bound to this thread. Try wrapping this call in JPAApi.withTransaction, or ensure that the HTTP context is setup on this thread."
. This is the code that's responsible for the actual call.
package actors.protocols;
import akka.japi.Option;
import model.DTO.AanleverAfspraakVO;
import model.domain.AanleverAfspraakDO;
import play.db.jpa.JPA;
import play.db.jpa.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.Collection;
public class AanleverAfspraakActorProtocol implements Protocol {
public static class GetAanleverAfspraakByID{
private int id;
public GetAanleverAfspraakByID(int id){
this.id = id;
}
/**
* Get AanleverAfspraak by id
* #return AanleverAfspraakVO or null
*/
#Transactional
public Option<AanleverAfspraakVO> getAanleverAfspraakById(){
final EntityManager entityManager = JPA.em().getEntityManagerFactory().createEntityManager();
final Query query = entityManager.createNamedQuery("findbyid");
query.setParameter("id", id);
final Collection<AanleverAfspraakDO> resultset = query.getResultList();
final Option<AanleverAfspraakVO> response;
Option<AanleverAfspraakVO> response_value = Option.none();
System.err.println("Size: " + resultset.size());
if(!resultset.isEmpty()){
try{
AanleverAfspraakDO aanleverAfspraakDO = resultset.iterator().next();
response_value = Option.some(this.convertAanleverAfspraakBOToVO(aanleverAfspraakDO));
}catch(Exception e){
response_value = Option.none();
e.printStackTrace();
}
finally {
try {
entityManager.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
response = response_value;
return response;
}
/**
* Convert AanleverAfspraakBO to AanleverAfspraakVO
* #param aanleverAfspraakDO
* #return
* #throws NullPointerException
*/
private AanleverAfspraakVO convertAanleverAfspraakBOToVO(AanleverAfspraakDO aanleverAfspraakDO) throws NullPointerException {
if(aanleverAfspraakDO == null){
throw new NullPointerException("aanleverAfspraakDO is null");
}
final AanleverAfspraakVO aanleverAfspraakVO = new AanleverAfspraakVO();
aanleverAfspraakVO.setId(aanleverAfspraakDO.getId());
aanleverAfspraakVO.setOmschrijving(aanleverAfspraakDO.getOmschrijving());
aanleverAfspraakVO.setIngangsDatum(aanleverAfspraakDO.getIngangsdatum());
aanleverAfspraakVO.setEindDatum(aanleverAfspraakDO.getEinddatum());
return aanleverAfspraakVO;
}
public int getId(){return id;}
}
}
Can someone please explain to me how I can use the build features of play for my JPA calls?
Everyone thanks for your help but I already solved this problem just a moment ago. I used the following code for my transaction
#Transactional
public Option<AanleverAfspraakDTO> executeTask() {
final Option<AanleverAfspraakDTO> response = api.withTransaction(() -> {
final Query query = api.em().createNamedQuery("findbyid").setParameter("id", id);
final Collection<AanleverAfspraakDO> resultset = query.getResultList();
the variable api is an instance of JPAApi
You shouldn't have to create an EntityManager. Play is doing this.
So in Play 2.4 and earlier this should just work:
final EntityManager entityManager = JPA.em().createNamedQuery("findbyid");
JPA.em() gives you the default EntityManager for this thread (https://www.playframework.com/documentation/2.4.x/api/java/index.html).
From Play 2.4 on you can inject Play's JPAApi although JPA still works fine:
#Inject
public MyClass(JPAApi api) {
this.jpaApi = api;
}
and then jpaApi.em() (https://www.playframework.com/documentation/2.5.x/api/java/play/db/jpa/JPAApi.html).

Categories