How to convert CRON string to ScheduleExpression in Java? - java

I got this problem:
I have a text field,
There should be a CRON expression written, and later on saved.
Now I need a method to convert the CRON string (here are some random examples: http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html) to java ScheduleExpression (http://docs.oracle.com/javaee/6/api/javax/ejb/ScheduleExpression.html)
But, I have no idea how to do it...
I have a timer based execution system, that run only on days, weeks and months, but now I need to implement the CRON models, so that the executions can be run on a specific period of time...
here is a little code, just to back me up:
#Resource
private TimerService timerService;
#Timeout
public void execute(Timer timer) {
Script s = (Script) timer.getInfo();
execute(s, true);
System.out.println("Timer Service : " + s.getScriptId());
System.out.println("Current Time : " + new Date());
System.out.println("Next Timeout : " + timer.getNextTimeout());
System.out.println("Time Remaining : " + timer.getTimeRemaining());
System.out.println("____________________________________________");
Date today = new Date();
if (s.getTimerSetup().getEndDate() <= today.getTime()) {
stopTimer(s);
}
}
#Override
public void startTimer(Script s) {
if (s.getTimerSetup().getTimerRepeat().equals("0")) {
return;
}
s.setStatus(true);
em.merge(s);
em.flush();
if (s.getTimerSetup().getEndDate() > System.currentTimeMillis()) {
long timeOut = 1L;
String timerRepeat = s.getTimerSetup().getTimerRepeat();
if (timerRepeat.equals("1")) {// day
timeOut = 1000L * 60L * 60L * 24L;
} else if (timerRepeat.equals("2")) {// week
timeOut = 1000L * 60L * 60L * 24L * 7L;
} else if (timerRepeat.equals("3")) {// month
timeOut = 1000L * 60L * 60L * 24L * 30L;
} else {
return; //Here is the part where the cron string is detected
}
long initialTimeOut = s.getTimerSetup().getStartDate() - System.currentTimeMillis();
if (initialTimeOut < 0) {
long initCheck = initialTimeOut * -1;
while (initCheck > timeOut) {
initCheck -= timeOut;
}
initialTimeOut = timeOut - initCheck;
}
Boolean found = false;
if (timerService.getAllTimers().size() == 0) {
System.out.println("Started the timer for the script: " + s.getFileName());
timerService.createTimer(initialTimeOut, timeOut, s);
} else {
for (Timer timer : timerService.getAllTimers()) {
if (((Script) timer.getInfo()).getScriptId() == s.getScriptId()) {
System.out.println("This script's timer was already started!");
found = true;
}
}
if (!found) {
System.out.println("Started the timer for the script: " + s.getFileName());
timerService.createTimer(initialTimeOut, timeOut, s);
found = true;
}
}
} else {
System.out.println("The script's end date has expired");
}
}
I marked the place where the cron string is detected (in the if statement)
And I need now to transform the string to a ScheduleExpression.
And after that to run it with the normal timers. (but that comes later :))
Please help. Thanks in advance.

I found the answer, but forgot to answer it, here is the code that worked for me:
private ScheduleExpression parseCronExpressionToScheduleExpression(String cronExpression) {
if ("never".equals(cronExpression)) {
return null;
}
// parsing it more or less like cron does, at least supporting same fields (+ seconds)
final String[] parts = cronExpression.split(" ");
final ScheduleExpression scheduleExpression;
if (parts.length != 6 && parts.length != 5) {
scheduleExpression = scheduleAliases.get(cronExpression);
if (scheduleExpression == null) {
throw new IllegalArgumentException(cronExpression + " doesn't have 5 or 6 segments as excepted");
}
return scheduleExpression;
} else if (parts.length == 6) { // enriched cron with seconds
return new ScheduleExpression()
.second(parts[0])
.minute(parts[1])
.hour(parts[2])
.dayOfMonth(parts[3])
.month(parts[4])
.dayOfWeek(parts[5]);
}
// cron
return new ScheduleExpression()
.minute(parts[0])
.hour(parts[1])
.dayOfMonth(parts[2])
.month(parts[3])
.dayOfWeek(parts[4]);
}
So, if you send the cron expression to the function, it will make a shedule expression out of it, but it does not work 100% on all cron expressions but on most
Here is what worked for the individual positions in the cron expression
* works
- works
, works
/ works
last works (only in the part Day Of Month)
What does not work are the letters, such as L, and the others, at least not when I last checked.
Hope this will help the next guy :)

Related

What is the best method of creating a server side timer in Java?

I am trying to make it so when a player is added to featured this timer counts down :P i have to init it in my main server so the timer will continue to count down even when players are offline. The timer needs to check if a player is assigned to a featured spot then begin counting down if not return of course :p
S
public static void featuredItems(Player c) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter time = DateTimeFormatter.ofPattern("H"); //Gets hours
int currentTime = Integer.parseInt(time.format(now));
int start1 = 22017, id = 0, totalSales = 0;
List<ListedItem> items = getSalesForPlayer(c.getName().toLowerCase());
c.getPA().sendFrame126("Featured Items", 22004);
for (ListedItem sale : items) {
if (sale.soldAll())
continue;
c.getPA().sendFrame126(sale.getSeller(), start1);//this calls for the top variable in featured shops
start1++;
c.getPA().sendFrame126("Time: " + (24 - currentTime) + " (H)", start1);//this calls for bottom varriable
start1 ++;
start1 ++;
totalSales++;
Misc.print("Name:" +c.getName().toLowerCase());
c.lastTimeForPost = now.getDayOfMonth();
Misc.print("Time:" +c.lastTimeForPost);
if (totalSales == 10) {
Misc.print("Reached 10 featured sales");
break;
}
}
}
private static final Runnable SERVER_TASKS = () -> {
try {
itemHandler.process();
playerHandler.process();
npcHandler.process();
shopHandler.process();
Highpkarena.process();
Lowpkarena.process();
globalObjects.pulse();
CycleEventHandler.getSingleton().process();
events.process();
serverData.processQueue();
} catch (Throwable t) {
t.printStackTrace();
t.getCause();
t.getMessage();
t.fillInStackTrace();
System.out.println("Server tasks - Check for error");
PlayerHandler.stream().filter(Objects::nonNull).forEach(PlayerSave::save);
}
};
i know i have to call it in my server main but just not sure how to do it or load it :p

java ScheduledFuture getDelay return negative value

I am using ScheduledExecutorService, Semaphore and ScheduledFuture to write a rate limiting function, simply put, when a client reaches the limit, server will return error 429 with "msg please try after %d second".
I use scheduledFuture.getDelay(TimeUnit.SECONDS) to get value of %d. For the first or second attempts, it acts normal, i.e. allow access unit reach the limit and showing how many seconds to wait afterward. Then getDelay starts showing negative value. Does it mean the ScheduledExecutorService not working properly?
following is the snippet
public RateLimiter(int permits, long durationInMillis){
this.semaphore = new Semaphore(permits);
this.permits = permits;
this.durationInMillis = durationInMillis;
scheduleReplenishment();
}
public boolean allowAccess() {
return semaphore.tryAcquire();
}
public long nextReplenishmentTime() {
return scheduledFuture.getDelay(TimeUnit.SECONDS);
}
public void stop() {
scheduler.shutdownNow();
}
public void scheduleReplenishment() {
scheduledFuture = scheduler.schedule(() -> {
semaphore.release(permits - semaphore.availablePermits());
}, durationInMillis, TimeUnit.MILLISECONDS);
}
If the task has done, the getDelay(TimeUnit) will be negative. To show it, I add two parameters to scheduleReplenishment(), and change getReplenishmentTime() to printReplenishmentTime().
Note1: If you create a Future<>, and replace one with another, you should care about the deleted one...
Note2: If you want test Future<> and Semaphore, don't release the allocated resources immediately.
private final ConcurrentSkipListMap<String, ScheduledFuture<?>> scheduledFutures
= new ConcurrentSkipListMap<>();
private final AtomicInteger counter = new AtomicInteger();
public void printReplenishmentTime() {
scheduledFutures.forEach((name, f) -> {
final long delay = f.getDelay(TimeUnit.SECONDS);
System.out.println(name + " delay " + delay);
});
}
/**
* try acquire one permit once from {#code semaphore},
* then wait {#code waitInMillis}, until all permits used.
*
* #param waitInMillis after successfully used one permit, wait
* #param permits all permits to use, best if permits #gt; 2
*/
public void scheduleReplenishment(final long waitInMillis, final int permits) {
final String name = "future" + counter.getAndIncrement();
scheduledFutures.put(name, scheduler.schedule(() -> {
try {
for (int permit = permits; 0 < permit;) {
final boolean ack = semaphore.tryAcquire(1);
System.out.println(name + " " + (ack ? "acquire" : "not acquire")
+ " one, but need " + permit);
if (ack) {
permit--;
}
if (0 < permit) {
try {
Thread.sleep(waitInMillis);
} catch (final InterruptedException e) {
System.out.println(name + " interrupted, exiting...");
return;
}
}
}
System.out.println(name + " done");
} finally {
semaphore.release(permits - permit);
}
// BAD CODE: semaphore.availablePermits() for debugging purposes
// only, maybe 0 release...
// semaphore.release(permits - semaphore.availablePermits());
}, durationInMillis, TimeUnit.MILLISECONDS));
}
scheduler.schedule() is a one time go function, that's why it shows negative getDelay() value.

Running a counter thread in the background during a game

I am wondering the best way to keep a timer going in the background while a game is played.
I am programming a version of the HiLo game (in Java), which gives a user a certain amount of time to determine a number. If a guess is not correct, the game will tell the user whether the name is too high or too low.
I'm keeping track of time using System.currentTimeMillis() and seeing how much time has elapsed. This seems to work well, and so far I have been checking to see how much time has elapsed each time a new number is entered. For example, currently the app output looks like this:
Welcome to HiLo!
You have 10 seconds to guess a number I'm thinking of between 1 and 100.
> 67
Too high.
> 29
Too low.
Half of your time is gone! Only 5 seconds remains!
> 37
Too high.
> 33
Oops! Time is up - try again.
As you can see, currently, it can only check when I enter a new number how much time is passed.
I have tried creating a thread to start with a timer, however, when I start it, it keeps counting until the time is exhausted, without going on to the thread.run(int guess) which will be run when there is a new guess. I want to be able to still make guesses while the counter runs. Here is my attempt at a new implementation for thread.start():
public void start(int time_sent) throws InterruptedException {
time = time_sent;
startTime = (System.currentTimeMillis() / 1000);
while (1==1) {
long elapsed = ((System.currentTimeMillis() / 1000) - (startTime));
if (elapsed >= (time)) {
System.out.println("Oops! Time is up - try again.");
System.exit(0);
}
else if (elapsed >= (time/2) && !halfWarning) {
System.out.println("Half of your time is gone! Only " + (time/2) + " seconds remains!");
halfWarning = true;
}
}
}
How can I continue running this counter in the background?
This is one more approach:
public void game() {
Scanner scanner = new Scanner(System.in);
int time = 10; // sec
message("You have " + time + " seconds to guess...");
new Thread(new Background(System.currentTimeMillis() / 1000, time)).start();
while (true) {
String s = scanner.next();
if (s.equals("55")) {
message("You win");
System.exit(0);
} else {
message("try again...");
}
}
}
private void message(String str) {
System.out.println(str);
System.out.print("> "); // monit
}
You start 1 thread with behavior implemented in Background class. Next you enter while loop to capture user inputs. The Background thread works in background...
private class Background implements Runnable {
private long startTime;
private long time;
private boolean halfWarning;
private Background(long startTime, long time) {
this.startTime = startTime;
this.time = time;
}
#Override
public void run() {
while (true) {
long now = System.currentTimeMillis() / 1000;
long elapsed = now - startTime;
if (elapsed >= (time / 2) && !halfWarning) {
message("\n Half of your time is gone! Only " + (time / 2) + " seconds remains!");
halfWarning = true;
}
if (elapsed >= time) {
message("\n Oops! Time is up - try again.");
System.exit(0);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
//ignore
}
}
}
}
Use a ScheduledExecutorService to execute concurrent actions in the future:
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> half = ses.schedule(new Runnable() {
#Override
public void run() {
System.out.println("Half of your time is gone!");
}
}, time / 2, TimeUnit.SECONDS);
ScheduledFuture<?> full = ses.schedule(new Runnable() {
#Override
public void run() {
System.out.println("Oops! Time is up - try again.");
// System.exit(0) ?
}
}, time, TimeUnit.SECONDS);
// check
if (input == toGuess) {
half.cancel();
full.cancel();
}
You could have a Timer thread that prints out these messages and shuts down the listening program.
It might inspire you :
public static class Game extends TimerTask {
private long start;
private long end;
public Game(long end) {
super();
this.start = System.currentTimeMillis();
this.end = end;
}
#Override
public void run() {
while (System.currentTimeMillis() - start < end)
System.out.println(System.currentTimeMillis());
}
}
public static void main(String[] args) {
TimerTask task = new Game(10000);
Timer timer = new Timer();
timer.schedule(task,0);
}

Parsing user time input in Java/GWT

What is the best way to parse time that a user typed in a text field in GWT? Default time formats require users to enter time exactly as the time format for locale specifies it.
I want to be more flexible as there are many different ways users can enter time. For example, entries like "8", "8p", "8pm", "8.15pm", "13:15", "1315", "13.15" should be valid.
I ended up with my own method that I want to share. This method returns time in milliseconds, which can be displayed using any data formats for the selected locale.
Any suggestions to improve it are highly appreciated.
EDIT: Improved following suggestions in comments.
public static Long parseTime(String value) {
// ";" is a common typo - we are not punishing users for it
value = value.trim().toLowerCase().replace(";", ":");
RegExp time12 = RegExp.compile("^(1[012]|[1-9])([:.][0-5][0-9])?(\\s)?(a|p|am|pm)?$");
RegExp time24 = RegExp.compile("^(([01]?[0-9]|2[0-3])[:.]?([0-5][0-9])?)$");
if (time12.test(value) || time24.test(value)) {
String hours = "0", minutes = "0";
if (value.contains(":") || value.contains(".")) {
String[] values = value.split("[:.]");
hours = values[0];
minutes = values[1].substring(0, 2);
} else {
// Process strings like "8", "8p", "8pm", "2300"
if (value.contains("a")) {
hours = value.substring(0, value.indexOf("a")).trim();
} else if (value.contains("p")) {
hours = value.substring(0, value.indexOf("p")).trim();
} else if (value.length() < 3) {
hours = value;
} else {
hours = value.substring(0, value.length() - 2);
minutes = value.substring(value.length() - 2);
}
}
if (value.contains("a") && hours.equals("12")) {
// 12am is actually zero hours
hours = "0";
}
Long time = (Long.valueOf(hours) * 60 + Long.valueOf(minutes)) * 60 * 1000;
if (value.contains("p") && !hours.equals("12")) {
// "pm" adds 12 hours to the total, except for 12pm
time += 12 * 60 * 60 * 1000;
}
return time;
}
return null;
}

problems converting CountDownTimer to my SimpleDateFormat output

I have a timer that counts down. I want the displayed format to be 00.00 or "ss.SS". However I haven't made any progress in hours. Without the SimpleDateFormat it displays 01.91 then goes to 01.9. This makes it hard to watch as it flickers to keep the view centered. All I really want is a way to keep the format 01.90 and not allow the 0 to be dropped. Could I accomplish this with my original code without the SimpleDateFormat?
/*
* This is my original code before I tried the SimpleDateFormat
*
* This code is fully functional and works good, it just keeps dropping the 0 every
* 10 milliseconds and makes the view shake
*
* getTimeSecs() could return 5, 10, 15, 30, 90 seconds converted to milliseconds
* getCountDownInterval() returns 10
*
*/
public void createTimer() {
myCounter = new CountDownTimer(getTimeSecs(), getCountDownInterval()) {
public void onTick(long millisUntilFinished) {
timerIsRunning = true;
if(millisUntilFinished < 10000) {
TVcountDown.setText("0" + ((millisUntilFinished / 10) / 100.0));
} else {
TVcountDown.setText("" + ((millisUntilFinished / 10) / 100.0));
}
} //end onTick()
#Override
public void onFinish() {
timerIsRunning = false;
TVcountDown.setBackgroundColor(myRes.getColor(R.color.solid_red));
TVcountDown.setTextColor(myRes.getColor(R.color.white));
TVcountDown.setText("Expired");
// Make sure vibrate feature is enabled
if(wantsVib == true) {
vib.vibrate(300);
}
} //end onFinish()
}.start();
} //end createTimer()
Here is my code after trying the SimpleDateFormat
public void createTimer() {
myCounter = new CountDownTimer(getTimeSecs(), getCountDownInterval()) {
public void onTick(long millisUntilFinished) {
timerIsRunning = true;
long current = (long) ((millisUntilFinished / 10) / 100.0);
TVcountDown.setText("" + timerDisplay.format(current));
}
#Override
public void onFinish() {
timerIsRunning = false;
TVcountDown.setBackgroundColor(myRes.getColor(R.color.solid_red));
TVcountDown.setTextColor(myRes.getColor(R.color.white));
TVcountDown.setText("Expired");
// Make sure vibrate feature is enabled
if(wantsVib == true) {
vib.vibrate(300);
}
}
}.start();
} //end createTimer()
I know! I don't even think I'm close to getting it with the SimpleDateFormat and I'm getting frustrated. It runs, but counts down only seconds, on the milliseconds side. So 15 seconds shows 00.15 not 15.00.
I don't expect someone to code it all out for me just need pointed in the right direction. All the tutorials I can find involve years, days, and such and I can't grasp the concept from that.
I'd prefer not to use the SimpleDateFormat -- cuz it hasn't been to simple for me -- and just use my original code and add a zero to the end of the milliseconds side every 10 milliseconds.
Thanks in advance.
Try this:
TVcountDown.setText(convertToMyFormat(millisUntilFinished));
and convertToMyFormat() method:
public String convertToMyFormat(long ms) {
String secString, msecString;
//constructing the sec format:
int sec = (int) (ms / 1000);
if(sec < 10) secString = "0"+sec;
else if(sec == 0) secString = "00";
else secString = ""+sec;
//constructing the msec format:
int msec = (int) ((ms-(sec*1000))/10.0);
if(msec < 10) msecString = "0"+msec;
else if(msec == 0) msecString = "00";
else msecString = ""+msec;
return secString+":"+msecString;
}
I'm not sure if I did the msec part correctly but you can tweek it as you want.
convert the number to a string and it will keep formatting. additionally you can do something like this
public String NumToStr(long i){
if (i < 10 ) {
return ("0" + Long.toString(i));
}
return Long.toString(i);
}
to make sure "9" will always come back as "09". Now set the string to the text.
actually what might be easier is this
if(millisUntilFinished < 10000) {
TVcountDown.setText("0" + Long.toString((millisUntilFinished / 10) / 100.0));
} else {
TVcountDown.setText("" + Long.toString((millisUntilFinished / 10) / 100.0));
}
Use Float.toString() or Double.toString, or whatever you need. Dont be afraid to write a little function to edit the string to make it appear as you want if you need to.
public String KeepFirstTwoCharOfString(String string){
//code to store first two Char into string
// return the string containing only first 2 chars
}

Categories

Resources