# Timer Bean



## peez (17. Mrz 2011)

Ich möchte gerne eine Timer Bean machen, die ab dem Deployment alle x Minuten was tut.
Jetzt finde ich im Netz (und in meinem schlauen Buch) nur Beispiele, wo der Timer durch einen Client gestartet wird.
Wie lässt sich das denn bewerkstelligen, dass der Timer direkt beim Deployment losgeht?


----------



## FArt (17. Mrz 2011)

Ab EJB 3.1 gibt es da was fertiges: New Features in EJB 3.1 - Part 2

In der Regel gibt es vorher schon AS spezifische Implementierungen.. z.B. die heißen gerne was mit "Scheduler Service" oder so... oder "Timer Service" ... eine Implementierung z.B. ist von Quartz, die findet im JBoss Verwendung.


----------



## peez (17. Mrz 2011)

Super, Danke! Ist ja ganz einfach 

Warum wird das jetzt immer mehrfach aufgerufen?

Hier mein erster Versuch:

```
@Stateless
public class DataInterfaceHousekeeperTimerBean {

	private final static Logger log = LoggerFactory.getLogger( DataInterfaceHousekeeperTimerBean.class );

	@Schedule( hour = "*", minute = "*", second = "1", persistent = false )
	private void doHousekeeping() {

		log.info( "********************************** HOUSEKEEPER *************************" );		
	}
}
```


----------



## FArt (17. Mrz 2011)

peez hat gesagt.:


> Super, Danke! Ist ja ganz einfach
> 
> Warum wird das jetzt immer mehrfach aufgerufen?
> 
> ...



Es wird mehrfach aufgerufen, weil das einem Scheduler entspricht und weil du es so haben wolltest.

Wenn du glaubst, dass das mehrfach aufgerufen wird, weil du für einen Zeitpunkt mehrere Logeinträgt hast, dann hast du dein Logging falsch konfiguriert.


----------



## peez (17. Mrz 2011)

FArt hat gesagt.:


> Es wird mehrfach aufgerufen, weil das einem Scheduler entspricht und weil du es so haben wolltest.


Mit mehrfach meine ich natürlich nicht jede Minute  Sondern jede Minute (wie eingestellt) dreifach direkt nacheinander.



> Wenn du glaubst, dass das mehrfach aufgerufen wird, weil du für einen Zeitpunkt mehrere Logeinträgt hast, dann hast du dein Logging falsch konfiguriert.


Hmm... Wenn ich debugge und an Zeile 9 nen Breakpoint setze, komme ich pro Durchlauf (Sprich jede Minute) dreimal an die Stelle. Ich glaube also nicht, dass es an falsch konfiguriertem Logging liegt...

In den ersten Versuchen hatte ich kein persistent=false drin. Könnten das noch Fragmente davon sein? Bin noch nicht so ganz schlau geworden, wie man einen einmal als persistent=true definierten Scheduler wieder los wird...
Wenn ich den jboss runterfahre und z.B. nach zehn Minuten wieder hochfahre, bekomme ich diese Ausgaben 10 mal sprich der Timer wird wiederholt für jedes mal wo er beim runtergefahrenen Server versäumt wurde. Hatte gedacht das würde durch persistent=false verhindert.


----------



## FArt (17. Mrz 2011)

Wenn bereits Tasks mit persisten=true liefen, dann kommen die natürlich nach einem Serverstart wieder zum Zuge.


----------



## peez (17. Mrz 2011)

> Wenn bereits Tasks mit persisten=true liefen, dann kommen die natürlich nach einem Serverstart wieder zum Zuge.


Und wie kriegt man die wieder weg?
clearTimers() in der MBean nützt leider nichts.

Lösche ich die vorher mit @Schedule annotierte Methode, gibts ne Exception beim Starten, dass die Methode nicht gefunden werden kann.
Nur wenn ich die komplette Klasse lösche kommt keine Exception mehr. Sobald ich wieder eine Klasse habe, die gleich heißt, kommt sie wieder.
(Aber das kann ja nicht die Lösung sein)...

Ich suche jetzt schon seit Stunden bei Google aber die Keywords "Timer Remove Persistent EJB" geben alle möglichen Ergebnisse nur nicht die, die helfen.


----------



## peez (17. Mrz 2011)

Keine Idee mehr?
Habe mittlerweile das Scheduler-Zeugs aufgegeben und rausbekommen wie ich nen normalen Timer automatisch erstellen kann:


```
@Singleton
@Startup
public class DataInterfaceTimerBean implements TimedObject {
	public final static String TIMER_NAME = "TestTimer";
	private final static Logger log = LoggerFactory.getLogger( DataInterfaceTimerBean.class );

	@Resource
	TimerService timerService;

	@Override
	public void ejbTimeout( Timer timer ) {
		log.info( "***************** ejbTimeout() *************" );
	}

	@PostConstruct
	public void installTimer() {
		log.info( "##Creating Timer for DataInterface##" );
		Timer timer = timerService.createTimer( 10000, 10000, TIMER_NAME );
	}
}
```


----------



## RaiausderDose (31. Aug 2011)

Hallo, 

Sorry, dass ich den alten Thread ausgrabe...

Ich benötige auch einen Timer, ABER ich muss in der Funktion auf Files zugreifen, da laut Spezifikation kein file.io in einer EJB erlaubt ist, habe ich dies in meiner Web-Bean (Managed Bean) gemacht (Datenbankzugriff etc. läuft über eine EJB).
Dort ist nun eine Funktion, welche Prozeduren vom SQL Server lädt, in Files schreibt und zipt und gewissen Leuten zu mailed, funktioniert alles wunderbar (ist als Backup gedacht).
Aber wie schaffe ich es nun, dass diese Funktion z.B. jeden Freitag (oder alle x Stunden) ausgeführt wird?

https://rz-static.uni-hohenheim.de/...el_09_012.htm#Rxx365java09012040002D91F04A100

Diese Variante kann ich wohl kaum nutzen, da dies ja eine run()-Funktion benötigt, die ich in der Managed Bean ja nicht zur Verfügung habe.

Irgendwelche Ideen / Ratschläge?

Vielen Dank für die Hilfe!


----------



## peez (31. Aug 2011)

java.io ist meines Wissens in der Spec nicht erlaubt, um dem Container Resource-Management (gleichzeitiger Aufruf, max. Anzahl von Channels, Transaktionen, ...) zu ermöglichen. Wenn das in einem sehr kontrollierten Rahmen passiert (wo du z.B. genau weißt, dass die Methode nur einmal vom Scheduler aufgerufen werden kann), würde ich mich jetzt fast aus dem Fenster lehnen und sagen, dass du auch (wenn du keinen Resource Adapter schreiben willst) die java.io Zugriffe benutzen kannst. Musst halt so stabil programmieren dass in allen Fällen (auch in allen Fehlerfällen) die Streams geschlossen u. Altdaten aufgeräumt werden.

Die Web-Bean aufrufen... Schwierig.. Wenn du von hinten durch die Brust willst, könnte man drüber nachdenken, per EJB Timer die entsprechende HTTP-URL aufzurufen wodurch die Methode dieser Web-Bean ausgelöst wird...

Aber wie gesagt (die alten Hasen dürfen mich gerne verbessern) würde ich jetzt sagen - am Ende schenkts sich nichts ob per Web-Bean oder EJB die java.io Aufrufe getätigt werden. Gibt in beiden Fällen kein Resource-Management.


----------



## RaiausderDose (31. Aug 2011)

Danke für die Antwort.
Dummerweise habe ich extra alles umgeschrieben, so dass in der EJB kein IO-Zugriffe entstehen.
Wäre nun aufwendig erneut alles umzuschreiben.

Die Lösung



```
import java.util.Timer;

public class TimerTaskDemo {
	
	
	public void go() {

		Timer timer = new Timer();
		timer.schedule(new WebClass(), 1000, 10000);		
	
	}
}
```

Und in der Webclass:


```
@Override
	public void run() {
	System.out.println("Los gehts!!");
	
	generateBackupMail(); // verschickt die Emails
		
	}
```

führt logischerweise zu diesem Fehler:

```
16:20:51,234 INFO  [STDOUT] Los gehts!!
16:20:51,234 ERROR [STDERR] Exception in thread "Timer-1" 
16:20:51,234 ERROR [STDERR] java.lang.NullPointerException
16:20:51,234 ERROR [STDERR] 	at de.procBackup.webapp.WebClass.getallDataBases(WebClass.java:162)
16:20:51,234 ERROR [STDERR] 	at de.procBackup.webapp.WebClass.getallProcedures(WebClass.java:169)
16:20:51,234 ERROR [STDERR] 	at de.procBackup.webapp.WebClass.startBackup(WebClass.java:45)
16:20:51,234 ERROR [STDERR] 	at de.procBackup.webapp.WebClass.generateBackupMail(WebClass.java:81)
16:20:51,234 ERROR [STDERR] 	at de.procBackup.webapp.WebClass.run(WebClass.java:300)
16:20:51,250 ERROR [STDERR] 	at java.util.TimerThread.mainLoop(Unknown Source)
16:20:51,250 ERROR [STDERR] 	at java.util.TimerThread.run(Unknown Source)
```

Da man ja nicht einfach eine neue Instanz anlegen kann, wie man die bestehende übergehen könnte, bin ich leider auch überfragt.


----------



## RaiausderDose (1. Sep 2011)

Ich hab eine Lösung gefunden, hurra 

Also ich erzeuge erstmal eine TimerTask Klasse, z.B. so :


```
import java.util.Timer;

public class TimerTaskDemo {
	
	public void go(WebClass beanReference) {
		Timer timer = new Timer();
		timer.schedule(beanReference, 1000, 10800000);		
		
	}
}
```

Wie man sehen kann, übergebe ich dieser eine beanReference, dies wäre die aktuelle Instanz der "WebKlasse", von der man ja keine neue Instanz erzeugen kann. (siehe obigen Beitrag).

In meiner Webclasse habe ich für den Timer die benötigte Funktion run() eingebaut:


```
@Override
	public void run() {
	System.out.println("Los gehts!!");	
	generateBackupMail(); // verschickt die Emails
		
	}
```

Diese wird ausgeführt, sobald der Timer bzw die TimerTaskDemo Klasse den jeweiligen Funktionsaufruf "bekommt".

Nun kommt der "Trick" 

Eine Funktion, die einmal bei Start der Applikation oder durch drücken eines CommandButtons ausgeführt wird, übergibt die aktuelle WebKlassen-Instanz an den Timer.

Dies macht man über ExternalContext usw:


```
public void letsgo() {

		System.out.println("Starte Timer");
		
		ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
		HttpSession session = (HttpSession) ec.getSession(true);
	        WebClass beanReference = (WebClass) session.getAttribute("webClass");
	    
	        TimerTaskDemo td = new TimerTaskDemo();
	    
// starte den Timer, welcher in der Webklasse die Funktion run() ausführt.
	        td.go(beanReference); 

		

	}
```

Eure Webklasse muss die TimerTask-Klasse vererbt bekommen.


```
public class WebClass extends TimerTask
```

webClass (session.getAttribute("webClass") ist der Name der managed bean in der face-config.xml .

So funktioniert der Java Timer auch wunderbar mit Webclassen bzw. managed beans.

Vielen Dank nochmal.


----------

