Spring Boot Progress report - java

I would like to be able to report a certain method's progress in Spring Boot. I used a seperate class which I store the current status in and return as the current view:
It looks like this:
public class SearchTableReloadState {
//STATIC STORAGE
public static long TABLE_ROW_COUNT = 0;
public static long CURRENT_OFFSET = 0;
public static long CURRENT_LIMIT = 0;
public static long DEFAULT_LIMIT = 20000;
public static void reset() {
TABLE_ROW_COUNT = 0;
CURRENT_OFFSET = 0;
CURRENT_LIMIT = DEFAULT_LIMIT;
}
public static void setDefaultLimit(long defaultLimit) {
DEFAULT_LIMIT = defaultLimit;
}
// VIEWMODEL
public long tableRowCount = 0;
public long currentOffset = 0;
public long currentLimit = 0;
public static SearchTableReloadState getState() {
SearchTableReloadState reloadState = new SearchTableReloadState();
reloadState.tableRowCount = TABLE_ROW_COUNT;
reloadState.currentOffset = CURRENT_OFFSET;
reloadState.currentLimit = CURRENT_LIMIT;
return reloadState;
}
}
And the methods:
#RequestMapping(value = {"/manage/searchtable/reload/state"}, method = RequestMethod.GET)
public #ResponseBody SearchTableReloadState searchTableReloadState() {
return SearchTableReloadState.getState();
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = {"/manage/searchtable/reload"}, method = RequestMethod.GET)
public void searchTableReload() throws ResourceAlreadyExistsException, ParameterMissingIdException {
SearchTableReloadState.reset();
SearchTableReloadState.TABLE_ROW_COUNT = productDataReferenceDao.countJobReferences();
productDataReferenceDao.truncateSearchTable();
while (SearchTableReloadState.CURRENT_OFFSET < SearchTableReloadState.TABLE_ROW_COUNT) {
... long running task
....
SearchTableReloadState.CURRENT_OFFSET += SearchTableReloadState.CURRENT_LIMIT;
}
}
The method with the /state would report the current state, so I could call these with Ajax on a site. Problem is, If I start the long running one, the state report request won't complete until the long running did not complete. I thought Spring uses separate threads for each request. Do I need to implement threading in Spring?
If I use the #Async annotation for the long running process, it works like I expected, but I still don't understand, why could two separate HTTP requests for a different method block each other!

If I use the #Async annotation on the method that is supposed to take a long time, the HTTP Request calling it will get a response immediately and it will run in the background and I can call the state method as I expected. Even though it is working, I still don't know why it won't work without the asynchronous execution.
If you want to use the #Async annotation, you have to put the #EnableAsync annotation on the class you used the #SpringBootApplication and/or #EnableAutoConfiguration.
I hope someone can provide a better answer later.

Related

How can I make a Java Spring Component class thread safe?

I have a spring class that when you call httpDatastoreFacade.getDatastore() it should give you the REST request thread safe datastore:
#Component
public class HttpDatastoreFacade {
private Boolean useAttribute = Boolean.FALSE;
public String getDatastore() {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextholder.currentRequestAttributes()).getRequest();
String datastore = request.getParameter("datastore");
if(useAttribute) {
datastore = String.valueOf(request.getAttribute("datastore"));
}
return datastore;
}
public void setDatastore(String datastore, Boolean useAttribute) {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextholder.currentRequestAttributes()).getRequest();
request.setAttribute("datastore", datastore);
this.useAttribute = useAttribute;
}
public Boolean getUseAttribute() {
return useAttribute;
}
}
Sometimes in my code I need to change that datastore but then I want to immediately change it back after I call whatever code needs the datastore differently:
#Component
public class someClass() {
#Autowired
private HttpDatastoreFacade datastoreFacade;
#Autowired
private OtherClass otherClass;
public void someMethod() {
String savedDatastore = datastoreFacade.getDatastore();
String savedUseAttribute = datastoreFacade.getUseAttribute;
//setDatastore to new settings
datastoreFacade.setDatastore("newStore", true);
//this is where I call my method's or methods that need this new datastore
otherClass.callSomeMethod();
//set the datastore back to old value
datastoreFacade.setDatastore(savedDatastore , savedUseAttribute );
}
}
My issue is that I'm running into threading problems where useAttribute is true but the datastore isn't set in the request attribute.
I'm looking for a better java pattern where I can lock the HttpDatastoreFacade while I do my otherClass.callSomeMethod() or whatever other calls I need to make until I set the HttpDatastoreFacade back to normal. otherCalss.callSomeMethod may be calling other methods that use HttpDatastoreFacade as well and they may want to set it how they need it. So maybe I need some short of datastore stack that is thread safe?
Seems a bean in #RequestScope could solve your problem.
#Component
#RequestScope
public class X {
//
}
you won't have to think about clearing the request scoped bean as you would the ThreadLocal. It will be collected when the corresponding ServletRequest is cleaned up.
I ended up making useAttribute a ThreadLocal variable which solved my problems.
private ThreadLocal<Boolean> useAttribute = new ThreadLocal<>();

Sending bulk mails periodically

I have a requirement where i have to send 100 mails/minute/user and it is time specific.(i.e All the mails i.e 100*1000 = 10000 mails/min should go on same time)
Currently there are almost 1000 users.
As well as for each email i am saving it first then sending it.
What things i need to implement for better performance and achieve goal optimally.
[Note: All the emails are sent via different accounts, so limit wont increase]
Any suggestion will be very helpful.
I am currently using Spring Boot for the project.
You can use Windows Service for this, if project run on windows server.
//Remember to set #EnableScheduling
//in the class containing your main method
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
}
#Component
public class ScheduledTasks {
List<Email> listOfEmails;
int nextBatch = 50;
int curBatch = 0;
//This method will run every 15 second.
#Scheduled(fixedDelay = 15000)
public void yourMethodName() {
//This will process all of your objects all at once using treads
for(int i = curBatch; i < maxBatchSize(); i++){
listOfEmails.get(i).process();
}
nextBatch+=50;
curBatch+=50;
}
private int maxBatchSize(){
if(nextBatch < listOfEmails.size()){
return nextBatch;
} else {
return listOfEmails.size();
}
}
}
public class YourObject {
Integer someTest = 0;
#Async
public void process(Email e) {
e.send();
}
}

How to write concurrent REST requests counter

I work on stress tests for REST server.
My aim is to create a mock controller method, which will throw 404 Error every 100 requests (other results are 200 OK), and check the total amount of sent requests and failed ones.
The problem is, even though I use ConcurrentHashMap and AtomicInteger for counting those figures, the amount of failed request varies +-20. Synchronization of RequestCounter.addFailed() didn't help. The only way I found is to synchronize controller's method, but it's not the option.
I run 220_000 stress test requests with 20 threads via Jmeter.
Here is my controller:
#RequestMapping(value = "/items/add", method = RequestMethod.POST)
public ResponseEntity addGDT(#RequestBody String data, Principal principal) {
RequestCounter.add();
if ((RequestCounter.getCounts().get("ADD").longValue() % 100) == 0) {
RequestCounter.addFailed();
return ResponseEntity.notFound().build();
} else {
return ResponseEntity.ok().build();
}
}
The number of requests is counted here:
public class RequestCounter {
static Map<String, AtomicInteger> counts = new ConcurrentHashMap<>();
static {
counts.put("ADD", new AtomicInteger(0));
counts.put("ADD_FAILED", new AtomicInteger(0));
}
public static void add(){
counts.get("ADD_GDT").incrementAndGet();
}
public static void addFailed(){
counts.get("ADD_FAILED").incrementAndGet();
}
UPDATE
I followed an advice of javaguy and refactored the code by removing map and working with AtomicInteger variables directly. But the result is still unpredictable: failedRequestCount still varies from +-3
public class RequestCounter {
static AtomicInteger failedRequestsCounter = new AtomicInteger(0);
...
public static void addGDTFailed(){
failedRequestsCounter.incrementAndGet();
}
UPDATE2
The situation wasn't resolved neither by calling directly the thread-safe variable, nor by separation and synchronization of a method for getting modulus
The problem is RequestCounter class is not threadsafe because of these two lines:
counts.get("ADD_GDT").incrementAndGet();
counts.get("ADD_FAILED").incrementAndGet();
These are NOT atomic operations i.e., actually, the computation involves two steps (read the value from Map and then write). Though ConcurrentHashMap and AtomicInteger are individually threadsafe, but when you use them collectively, you need a synchronization or locking.
But you can achieve what you wanted for your testing with a much simpler code without using a ConcurrentHashMap itself.
To make the RequestCounter class threadsafe, just remove the Map, and directly access the AtomicInteger reference as below:
public class RequestCounter {
private final AtomicLong addInt = new AtomicLong();
private final AtomicLong addFailed = new AtomicLong();
public static long get() {
return addInt.get();
}
public static long add() {
return addInt.incrementAndGet();
}
public static long addFailed(){
return addFailed.incrementAndGet();
}
}
UPDATE1: Problem with 3% variation of requests:
You need to esnure that RequestCounter.add() is being called only once per request, look at my controller code below:
#RequestMapping(value = "/items/add", method = RequestMethod.POST)
public ResponseEntity addGDT(#RequestBody String data, Principal principal) {
if ((RequestCounter.get() % 100) == 0) {
RequestCounter.addFailed();
return ResponseEntity.notFound().build();
} else {
RequestCounter.add();
return ResponseEntity.ok().build();
}
}

how to invoke schedular every day

what i do is ,when i run first time a servlet (which is invoked from jsp) that while put an entry of that service,daily in conf file.i want to run a scheduler which will invoke program(servlet- which runs and send mail) for that service daily 10 .
below is the code i use to execute a task.but problem is when i stop the server ,the scheduler stops and nothing happens
public class Schedule
{
public static final String CONF_PATH = "../webapps/selen/WEB-INF/scheduling.properties";
public static Properties schProps = null;
public static FileInputStream sis = null;
public static long period;
public static Timer timer = new Timer();
public static String servicename = null;
public static String keyValues = null;
public static String reValues[] = null;
public static String schedulingValue = null;
public static String service_url = null;
public static String browserlist = null;
public static String testType = null;
public static String mailCheacked = null;
public static String toaddr = null;
public static HttpServletRequest request = null;
public static HttpServletResponse response = null;
public static String serversURL = null;
public static String contextPath = null;
public static Date delay = null;
public void scheduleLoad(String serviceValue) throws Exception
{
try
{
schProps = new Properties();
sis = new FileInputStream(CONF_PATH);
schProps.load(sis);
servicename = SServlet.serviceName;
keyValues = schProps.getProperty(serviceValue);
reValues = keyValues.split(",");
String request = reValues[0];
String response = reValues[1];
schedulingValue = reValues[2];
service_url = reValues[3];
browserlist = reValues[4];
testType = reValues[5];
mailCheacked = reValues[6];
toaddr = reValues[7];
serversURL = reValues[8];
contextPath = reValues[9];
if(reValues[2].equals("Daily"))
{
Calendar cal =Calendar.getInstance();
cal.set(Calendar.HOUR,10);
cal.set(Calendar.MINUTE,20);
cal.set(Calendar.SECOND,0);
delay = cal.getTime();
period = 1000 * 60 * 60 * 24;
schedule();
}
else if(reValues[2].equals("Stop"))
{
stop();
}
}
catch(NullPointerException npe)
{
System.out.println("null point exception ");
}
finally
{
if(sis !=null)
{
sis.close();
}
}
}
public static void schedule()
{
MyTimerTask mt = new MyTimerTask(request,response,servicename,service_url,browserlist,mailCheacked,testType,schedulingValue,toaddr,serversURL,contextPath);
timer.schedule(mt,delay,period);
}
public static void stop()
{
timer.cancel();
}
}
class MyTimerTask extends TimerTask
{
public HttpServletRequest request;
public HttpServletResponse response;
public String servicename;
public String service_url;
public String browserlist;
public String mailCheacked;
public String testType;
public String schedulingValue;
public String toaddr;
public String serversURL;
public String contextPath;
public MyTimerTask(HttpServletRequest request,HttpServletResponse response, String servicename,String service_url,String browserlist,String mailCheacked,String testType,String schedulingValue,String toaddr,String serversURL, String contextPath)
{
this.request = request;
this.response = response;
this.servicename = servicename;
this.service_url = service_url;
this.browserlist = browserlist;
this.mailCheacked = mailCheacked;
this.testType = testType;
this.schedulingValue = schedulingValue;
this.toaddr = toaddr;
this.serversURL = serversURL;
this.contextPath = contextPath;
}
public void run()
{
SServlet sservlet = new SServlet();
sservlet.sServerloading(request,response,servicename,service_url,browserlist,mailCheacked,testType,schedulingValue,toaddr,false,1,serversURL,contextPath);
}
}
The JDK Timer runs in the JVM, not in the operating system. It's not CRON or Windows scheduler. So when you stop your server (Tomcat? JBoss? Glassfish?), you are effectivly stopping the JVM that the Timer lives in so of course it won't run any more. If you want a timer (scheduler) that runs independently of your server, you will have to start it in it's own JVM, either as a standalone java program using the java command or inside another server instance.
On a side note, if you're open to some critique, a small review of your code:
Avoid mixing static and non-static contexts if possible. Your Schedule class instance method scheduleLoad() makes heavy use of static member variables for statefull storage. Variables are either only used in the execution of a method (in which case they should be declared inside that method) or they are used to describe the state of an object (in which case they should be private instance members of the class) or they are global constants or immutable global variables (in which case they should be declared static final). Exceptions to these exist, but are less common.
Avoid declaring member variables public if they are not also final. Adhere to the JavaBean pattern, use getters and setters. If a variable is, in reality, a constant then it should be public static final.
Avoid using classes or parameters out of scope. For instance, your MyTimerTask uses HttpServletRequest and HttpServletResponse as member variables and method parameters. This makes no sense as MyTimerTask is not used in the scope of a servlet request (and will subsequently always be null, right?). Or, if that is indeed the case, if you are explicitly setting the static members of the Schedule in some servlet and then invoking scheduleLoad(), see my first point about improper use of static context. Your code would not be thread-safe and concurrent invocation of whichever servlet that uses the Schedule would produce unpredictable behaviour.
UPDATE:
It's hard to know where to start as I'm not sure what your level of expertise is in Java. If you are unfamiliar with how to execute stand-alone java applications, I would suggest having a go at some tutorials. Oracle has a bunch at http://download.oracle.com/javase/tutorial/. http://download.oracle.com/javase/tutorial/getStarted/index.html is a good place to start as it walks you through a very basic "hello world" type application with a main method and how to execute it using the java command, as well as some common mistakes and problems.
Once you've figured all that out, take a few minutes to figure out what your application should do, which resources it will require and if it needs to call any "external" systems. You mentioned that it should "execute a servlet to send mail". Does that mean that it has to call a specific servlet or is it just the sending mail that is really what you are after. In that case, maybe you can just move all the mail sending logic to your standalone program? If not, you will have to call the servlet using a http request (like a browser would). There are a number of existing frameworks for doing things like that. Apache HttpClient is a very popular one.
If you stop program it does not work. It is not a bug. It is a feature. BTW if you shutdown your computer nothing happens too :).
But If you questing is how to make my scheduled task more robust, e.g. how to make task to continue working when server stops and then starts again you have to persist somewhere the state of you scheduler, i.e. in your case the last time of task execution. You can implement this yourself: create special file and store the data there. You can use cross platform pure java Preferences API to do this: the data will be stored in file system in Unix and Registry in windows. you can save state in DB too.
But you can use other products that have already implemented this functionality. The most popular and well-known is Quartz.
But Quartz still need some java process to be up and running. If you want to be able to run your tasks even if no java process is running use platform dependent tools: cron tab for Unix and scheduler API for windows (it is accessible via VBScript, JScript, command line).
Unix has cron

Expose current progress of an #Asynchronous function to use in View

In my JEE6-App (running on Glassfish 3.0.1) I have an EmailEJB which has to send lots of mails. The mails are sent asynchronously, so its annotated with the new EJB3.1 #Asynchronous, letting it be run in a separate Thread. Now i want the user to be informed about the current status of the method: How many mails have already been sent?
Sending the mails asynchronously works fine, but i can't figure out how to let the progress be accessible from outside. Seems like my approach to do that is quite wrong, but somehow it has to be possible (maybe another approach). This is how my EmailEJB currently looks like (its kind of pseudo code, but explains what i want):
#Stateful
public class EmailEJB {
#Asynchronous
public Future<Integer> sendMails() {
for (int i=0; i<mails.size; i++) {
sendMail(mails[i])
// i want to return the progress without returning ;)
return new AsyncResult<Integer>(i)
}
}
}
//Just for the completeness... from outside, i'm accessing the progress like this:
Future<Integer> progress = emailEJB.sendEmails();
Integer currentvalue = progress.get();
How can i return the current progress inside my asynchronous function, without cancelling it with a return? How can i show the user the progress of a loop inside a function? Do i need another asynchronous method? Any hints?
Nobody? Ok so this is my solution. Im not sure if this is a big fat workaround or just a way to get this done.
Since an #Asynchronous method cannot access the Session context, and therefore also no Session Beans (at least i dont know how, i always got ConcurrentModificationErrors or similar ones) i created a Singleton ProgressEJB, which contains a HashMap:
#Singleton #LocalBean #Startup
public class ProgressEJB {
private HashMap<String, Integer> progressMap = new HashMap<String, Integer>
// getters and setters
}
This hashmap should map the SessionId (a String) to an Integer value (the progress 0->100). So a user session is associated with a progress.
In my EmailEJB, i'm injecting this ProgressEJB, and in my #Asynchronous method, i'm increasing the value everytime an email has been sent:
#Stateful #LocalBean
public class EmailEJB {
#Inject
private ProgressEJB progress;
// Mail-Settings
...
#Asynchronous
public void sendEmails(user:User, message:Message, sessionId:String) {
progress.progressMap.put(sessionId, 0);
for (int i=0; i<mails.size; i++) {
sendMail(mails[i])
progress.getProgressMap().put(sessionId, (i / mails.size) * 100)
}
progress.getProgressMap().remove(sessionId);
}
The sessionId comes from my Managed (Weld) Bean, when calling the function:
#SessionScoped
#Named
public class EmailManager {
#Inject
private ProgressEJB progress;
#Inject
private FacesContext facesContext;
private String sessionId;
#PostConstruct
private void setSessionId() {
this.sessionId = ((HttpSession)facesContext.getExternalContext().getSession(false)).getId();
}
public Integer getProgress() {
if (progress.getProgressMap().get(sessionId) == null)
return 100;
else
return progress.getProgressMap().get(sessionId);
}
}
Now i can access progress from EmailManager from my JSF view with Ajax Polling, telling the user how many mails already have been sent. Just tested it with 2 users, seems to work.
I also see only a #Singleton solution here.
But this imply the need of Housekeeping in ProgressEJB. E.g. some effort is needed to prune old session from Hashmap.
Another solution is described in
Is there any way to know the progress of a EJB Asynchronous process?
This solution does not need a Stateful Bean.
#Stateless
public class EmailEJB {
// Mail-Settings
...
#Asynchronous
public void sendEmails(User user, Message message, WorkContext context) {
progress.progressMap.put(sessionId, 0);
for (int i=0; i<mails.size; i++) {
sendMail(mails[i])
context.setProgress((i / mails.size) * 100)
}
context.setRunning(false);
}
}
The Context-Object, which holds the progress.
public class WorkContext {
//volatile is important!
private volatile Integer progress = 0;
private volatile boolean running = false;
// getters & setters
}
The usage is very easy.
#SessionScoped
#Named
public class EmailManager {
#Inject
private EmailEJB emailEJB;
private WorkContext workContext;
public void doStuff() {
workContext = new WorkContext();
emailEJB.sendEmails(user, message, workContext)
}
public Integer getProgress() {
return workContext.getProgress();
}
....
}

Categories