4

Task - Turn a bulb on and off at a specified time during a day. I need to know how to fix my code as per the information given below. I also need to know if I am using the timer class correctly, that is, is my code design correct ? The code may work but it could be bad design which will cause problems later. I don't want that to happen.

Output is (This is not the output i really wanted :( ) -

This is the main program
Current time is - xxx
Future time is - xxx+5sec
Future time is - xxx+10sec
Main program ends
Bulb B1 is OFF

Desired output -

This is the main program
Current time is - xxx
Future time is - xxx+5sec
Future time is - xxx+10sec
Bulb B1 is ON  //first on
Bulb B1 is OFF //then off
Main program ends//This should always be in the end.

How do I fix the code below to get what I want ?

Bulb Class

class Bulb {

private boolean state = false;//On or off
private String name;

Bulb(String name){

    this.name = name;

}

public void setState(boolean state){

    this.state = state;
    if(this.state == true){

        System.out.println("Bulb " + name + " is ON");

    }else{

        System.out.println("Bulb " + name + " is OFF");

    }

}


public boolean getState(){
    return this.state;

}


}

BulbJob class which is a TimerTask

import java.util.*;

class BulbJob extends TimerTask{

private Bulb bulbToHandle;
private boolean setBulbStateEqualTo;

BulbJob(Bulb toHandle){

    this.bulbToHandle = toHandle;

}


//NOTE: Must be called before run(), otherwise default value is used
public void setBulbStateEqualTo(boolean setBulbStateEqualTo){

    this.setBulbStateEqualTo = setBulbStateEqualTo;

}


//NOTE: call run() only before calling above method
public void run(){

    this.bulbToHandle.setState(setBulbStateEqualTo);//Set on or off

}

}

BulbScheduler class - this schedules when the bulb is turned on or off.

import java.util.*;

@SuppressWarnings( "deprecation" )
class BulbScheduler {

public static void main(String args[]) throws InterruptedException{

    System.out.println("This is the main program");

    Timer time = new Timer();
    Bulb b1 = new Bulb("B1");
    BulbJob bj = new BulbJob(b1);

    bj.setBulbStateEqualTo(true);//Task - Turn bulb on at time = afterCurrent

    Date current = new Date();//Get current time and execute job ten seconds after this time
    Date afterCurrent = (Date) current.clone();

    System.out.println("Current time is - " + current);

    int currentSecs = current.getSeconds();
    int offset = 5;//number of seconds

    afterCurrent.setSeconds(currentSecs + offset);
    System.out.println("Future time is - " + afterCurrent);

    time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

    //Now turn the bulb off at new time = newest afterTime
    afterCurrent.setSeconds(currentSecs + 2 * offset);
    System.out.println("Future time is - " + afterCurrent);

    bj.setBulbStateEqualTo(false);//Task - Now turn the bulb off at time = afterCurrent

    System.out.println("Main program ends");

}

}
asgs
  • 3,928
  • 6
  • 39
  • 54
Time
  • 1,551
  • 2
  • 13
  • 14
  • +1 for choosing `TimerTask` over plain `Thread`'s `sleep()`. – asgs Mar 11 '13 at 08:25
  • @asgs - how do I make "main program ends" come only after everything is executed ? – Time Mar 11 '13 at 08:34
  • 1
    Since the main thread doesn't depend on your Jobs, I'm afraid you'd to have redesign such that you could use the `Thread`'s `join()` method to wait for the TimerTask to complete. – asgs Mar 11 '13 at 08:43
  • @asgs - So, BulbJob extends Thread??? But then, I will lose all the TimerTask functionality. How to I fix that? :( – Time Mar 11 '13 at 08:49
  • @asgs - Also, this is what i want to do - turn the bulb on at currentTime + 5 sec. Then, turn the same bulb off at currentTime + 10sec. Do I have to create new BulbJob or Timer to turn it off ? – Time Mar 11 '13 at 08:52
  • Date.setSeconds does not work as expected. use calender.roll and print the time the task is done. see any of my two solutions below – tgkprog Mar 12 '13 at 07:57

4 Answers4

2

This section:

time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

//Now turn the bulb off at new time = newest afterTime
afterCurrent.setSeconds(currentSecs + 2 * offset);

only schedules one task. If you need to schedule it twice, do so explicitly:

time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

//Now turn the bulb off at new time = newest afterTime
afterCurrent.setSeconds(currentSecs + 2 * offset);
time.schedule(bj, afterCurrent);//Schedule job "bj" at time = afterCurrent

Also. this line:

bj.setBulbStateEqualTo(false);

is executed in the main thread, so it will before both tasks. You should schedule that statement to run between the two tasks.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • 1
    Also, this is what i want to do - turn the bulb on at currentTime + 5 sec. Then, turn the same bulb off at currentTime + 10sec. Do I have to create new BulbJob or Timer to turn it off ? – Time Mar 11 '13 at 08:52
  • 1
    Create 3 tasks: turnBulbOn, turnBulbOff, ExitProgram, that do these things and schedule them one after the other. You only need one timer. – assylias Mar 11 '13 at 08:52
  • There you go. Making the program exit with your custom message as a separate `TimerTask` is the best bet. – asgs Mar 11 '13 at 08:54
  • @assylias - how do i make the ExitProgram task? everything else is working as per plan. I added the modified code as my answer. – Time Mar 11 '13 at 09:01
  • only partly correct. setSecond is not the way to go. try with a date time starting at 2:59:59 wont go to 3:00:00. See other answers, use Calendar.roll see api javadoc of java.util.Date#setSecond – tgkprog Mar 11 '13 at 16:57
0

Code is fixed, but this version cannot exit main in the end -

import java.util.*;

@SuppressWarnings( "deprecation" )
class BulbScheduler {

public static void main(String args[]) throws InterruptedException{

    System.out.println("This is the main program");

    Timer timeOn = new Timer();
    Timer timeOff = new Timer();
    Bulb b1 = new Bulb("B1");
    BulbJob bjOn = new BulbJob(b1);
    BulbJob bjOff = new BulbJob(b1);

    bjOn.setBulbStateEqualTo(true);//Task - Turn bulb on 
    bjOff.setBulbStateEqualTo(false);//Task - Then turn the bulb off later

    Date current = new Date();//Get current time and execute job ten seconds after this time
    Date afterCurrent = (Date) current.clone();

    System.out.println("Current time is - " + current);

    int currentSecs = current.getSeconds();
    int offset = 3;//number of seconds

    afterCurrent.setSeconds(currentSecs + offset);
    System.out.println("Future time is - " + afterCurrent);

    timeOn.schedule(bjOn, afterCurrent);//Schedule job "bj" at time = afterCurrent

    //Now turn the bulb off at new time = latest afterCurrent
    afterCurrent.setSeconds(currentSecs + 2 * offset);
    System.out.println("Future time is - " + afterCurrent);

    timeOff.schedule(bjOff, afterCurrent);

    System.out.println("Main program ends");

}

}
Time
  • 1,551
  • 2
  • 13
  • 14
  • You are printing `Main program ends` in the main thread - it will execute immediately... If you want it to be delayed, you need to make a task with it and run it after turning the bulb off. – assylias Mar 11 '13 at 09:07
  • @assylias - okay. I could make the EnProgram task. But, i will have to run it only after bjOff. For that I have to keep track of all timings which is very inconvenient. Unfortunately, there is no join() method for TimerTask, ie finish task1 only after task2 completes. – Time Mar 11 '13 at 09:10
  • 1
    You could also use a blocking mechanism, for example a CountdownLatch. – assylias Mar 11 '13 at 09:49
  • 1
    this is a bad implementaion. setSecond is not the way to go. try with a date time starting at 2:59:59 wont go to 3:00:00. See other answers, use Calendar.roll – tgkprog Mar 11 '13 at 16:56
0

you are not setting the time correctly. Need to use GreogarianCalendar.

java.util.Date is used but cannot use its setSeconds Read the Javadoc its pretty good and will help a lot. public void setSeconds(int seconds)

Deprecated. As of JDK version 1.1, replaced by Calendar.set(Calendar.SECOND, int seconds). Sets the seconds of this Date to the specified value. This Date object is modified so that it represents a point in time within the specified second of the minute, with the year, month, date, hour, and minute the same as before, as interpreted in the local time zone.

You need to use java.util.GregorianCalendar # add(Calendar.SECOND, howManySeconds)

then use getDate() to get the Date object and send it to the Timer.

calling setSecond on a date wont change the other fields. see the java doc of Calendar.add and roll. http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Calendar.html and see the rules in the class inro.

tgkprog
  • 4,493
  • 4
  • 41
  • 70
0

Can also use the timer object's schedule(TimerTask task, long delay) Schedules the specified task for execution after the specified delay (milliseconds). Modified code -

import java.util.*;

class BulbScheduler {

    private static java.text.SimpleDateFormat sdf1 = new java.text.SimpleDateFormat ("yy MM dd HH mm ss");

//helper    
static String formatDate(Date d){
        return sdf1.format(d);
    }

    public static void main(String args[]) throws InterruptedException{
        System.out.println("This is the main method");
        java.util.GregorianCalendar cal = new java.util.GregorianCalendar();

        Bulb b1 = new Bulb("bulb 1", false);
        Bulb b2 = new Bulb("bulb 2", false);

        System.out.println("Time now " + formatDate(cal.getTime()));

        Timer timer = new Timer("bulbs");
        BulbJob b1On = new BulbJob(b1, true);
        BulbJob b1Off = new BulbJob(b1, false);
        BulbJob b2On = new BulbJob(b2, true);
        BulbJob b2Off = new BulbJob(b2, false);
        timer.schedule(b1On, 3 * 1000);//after 3 seconds
        timer.schedule(b2On, 7 * 1000);//after 4 seconds
        timer.schedule(b1Off, 6 * 1000);//after 6 seconds; before b2 on

        b1On = new BulbJob(b1, true);
        timer.schedule(b1On, 9 * 1000);


        //if you want main to wait need to add code here to make it wait,
        // but even if does the JVM wont exit. Its just a method. The JVM exits when all non daemon threads are done
        // or System.exit is called

        System.out.println("This is the main method ending; but other threads might be running ...");
        //main thread JVM waits for all other non dameons to end

    }

}

Changed BulbJob

import java.util.*;

class BulbJob extends TimerTask{

private Bulb bulbToHandle;
private boolean bulbNewState;//dont start propert names with set

//why a seperate property when we need to set the new state everytime and cannot reuse jobs?
BulbJob(Bulb toHandle, boolean newState){
    this.bulbToHandle = toHandle;
    bulbNewState= newState;
}

public void run(){
    this.bulbToHandle.setState(bulbNewState);//Set on or off
}

}

class Bulb ... public void setState(boolean state){ this.state = state; System.out.println("Bulb " + name + " is " + (state ? "on" : "off") + " at " + BulbScheduler.formatDate(new java.util.Date()));//if okay too

}

tgkprog
  • 4,493
  • 4
  • 41
  • 70