Quartz Scheduler und UseCase

  • Themenstarter Gelöschtes Mitglied 5909
  • Beginndatum
G

Gelöschtes Mitglied 5909

Gast
Hallo Zusammen,

da ich mit Quartz bisher noch nix gemacht habe und einen Scheduler für einen bestimmten UseCase brauche,
würde es mich freuen wenn ihr mir sagen könntet, ob ich meinen UseCase überhaupt damit abbilden kann
(Damit ich keine Zeit "verschwende").

Folgende Situation:

Anwender (keine Informatiker) sollen bestimmte Aufgaben schedulen können. D.h. die Anwender möchten,
das ein bestimmter Vorgang (Task) an einem definierten Zeitpunkt (auch mehrmals) ausgeführt werden soll.

Ein Task selber ist allerdings kein "runnable" oder etwas Ähnliches, sondern definiert nur, was ausgeführt werden soll.
D.h. In einem Task können mehrere "runnables" stehn, die zum selben Zeitpunkt ausgeführt werden sollen.

Desweiteren soll ein Task so Konfiguriert werden können, dass er nur ausgeführt wird, wenn der Vorherige Task erfolgreich ausgeführt wurden. Ein Task ist erfolgreich, wenn alle "runnables" des Tasks erfolgreich waren.

Außerdem müssen Anwender den Ausführungzeitpunkt verändern können.

Beispiel (vereinfacht)

Code:
Task 1                        Task 2                                                Task3
Runnable1...n                 Runnable1...n                                         Runnable1..n
parent:null                   parent:Task1                                          parent: Task2
pattern:01.01.2010            pattern: null (wenn task 1 fertig ist)                pattern:null (wenn Task2 fertig ist)

Die begriffe Task und Runnable sind logische Anwendungsbegriffe und haben mit Quartz nix zu tun.

Das Pattern von Task 1 muss zu Laufzeit verändert werden können.

Außerdem muss gewährleistet sein, dass der Scheduler die Tasks startet, auch wenn er zuvor neu gestartet wurde.

Ist so etwas möglich? Insbesondere das "chaining" erscheint mir sehr tricky.

Wäre super, wenn jemand ein kleines Beispiel hätte.
 

kay73

Bekanntes Mitglied
Quartz kann Dich gut unterstützen, da dessen JobStore datenbankbasiert und damit robust ist. Da Quartz zu großen Teilen auch Cron nachbaut, ist auch gewährleistet, dass die Jobs laufen, wenn Quartz mal neugestartet wurde.

Sicherlich wird Quartz in Deinem Projekt nur eine untergeordnete Rolle als "dummer Pulsgenerator" spielen, denn die Logik, die Du haben willst, bildet Quartz nicht ab. So wie sich das anhört, käme je nach Komplexität ein Business-Prozess-Management System wie Activiti in Frage. Könnte mir vorstellen, dass man eine hübsche GUI stricken könnte, mit der man Trigger und Prozessbeschreibungen abstrahieren kann. Aber das Ändern zur Laufzeit wird happig. Wüsste nicht, wie ich das machen würde...
 
Zuletzt bearbeitet:
G

Gelöschtes Mitglied 5909

Gast
Das mit dem "Pulsgenerator" hab ich mir fast schon gedacht. Eine BPM Engine ist aber warscheinlich schon wieder viel zu viel, muss mich nochmal umschaun. Problem an Quartz ist denk ich mal, dass ich den CronTrigger nicht ändern kann. Bzw. ich kriege es nicht mit
 

kay73

Bekanntes Mitglied
Du kannst den Job einfach
Code:
reschedule()
-n:
QuartzScheduler (Quartz Parent POM 1.8.1 API))

Übrigens zum "chaining": Das lässt sich sehr elegant mit der ConcurrencyApi implementieren.

In
Code:
App
wird eine Sequenz aus zwei Tasks gebaut, die jeweils aus zwei "Untertasks" bestehen, die gleichzeitig laufen. Einer der beiden Untertasks schlägt im Schnitt jedes zweite Mal fehlt, was bedeutet, dass der Folge(-ober-)task nicht läuft. Du müsstest jetzt noch ein paar hübsche Factorymethoden für Tasks und Untertasks bauen und die Erzeugung persistieren.

Ausserdem sollten die Tasks noch einen "Kontext" injiziert bekommen, damit sie sinnvoll arbeiten können.

Code:
TaskSequenceImpl
würde dann wohl das Quartz-Job-Interface implementieren und
Code:
TaskSequenceImpl.runTasks()
wäre die Methode, die der Quartz Scheduler triggern würde.

Java:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;

class TaskSequenceImpl {
	
	private static final Logger logger = Logger.getLogger(TaskSequenceImpl.class.getSimpleName());
	
	public boolean runTasks(final List<TaskImpl> tasks) {
		for(final TaskImpl task : tasks) {
			try {
				task.execute();
			} catch(final Throwable ex) {
				logger.severe("Task "+task+" throw exception. Aborting because of " + ex.getMessage());
				return false;
			}
		}
		
		return true;
	}
}

class TaskImpl {
	static ExecutorService EXS = Executors.newCachedThreadPool();	
	
	final Lock runLock = new ReentrantLock(); 
	
	final List<Callable<?>> callables = new ArrayList<Callable<?>>();
	
	public TaskImpl(final Callable<?> ...callables ) {
		for(final Callable<?> c : callables) {
			append(c);
		}
	}
	
	public void append(final Callable<?> callable) {
		if(!runLock.tryLock()) { 
			throw new IllegalStateException("Task is running!");
		}
		try {
			callables.add(callable);
		} finally {
			runLock.unlock();
		}
	}
	
	public void execute() throws InterruptedException, ExecutionException {
		if(callables.isEmpty()) {
			return;
		}
		
		try {
			runLock.lock();
			
			final List<Future<?>> futures = new ArrayList<Future<?>>(callables.size());
			for(final Callable<?> c : callables) {
				futures.add(EXS.submit(c));
			}
			
			while(!futures.isEmpty()) {
				final Iterator<Future<?>> futureIterator = futures.iterator();
				while(futureIterator.hasNext()){
					final Future<?> f = futureIterator.next();
										
					f.get();
					// do something with the result here...
					
					futureIterator.remove();
				}
			}
		} finally {
			runLock.unlock();
		}
	}
}

public class App {

	private static final Logger logger = Logger.getLogger(App.class.getSimpleName());
	
	public static void main(String[] args) {
		final TaskImpl [] tasks = {
			new TaskImpl(
					
//				wartet ein bisschen und gibt "42" zurück
				new Callable<Integer>() {

					@Override
					public Integer call() throws Exception {
						Thread.sleep(500L);
						logger.info("task1 Callable<Integer> returning 42.");
						return 42;
					}					
				},

//				Wirft bei jedem zweiten Mal eine exception, simuliert einen
//				fehlerhaften Task.
				new Callable<Boolean>() {

					@Override
					public Boolean call() throws Exception {
						final Random random = new Random();
						if(!random.nextBoolean() ) {
							throw new RuntimeException("task1 Callable<Boolean>: I just felt like throwing up...");
						}
						logger.info("task1 Callable<Boolean> returning something...");
						return random.nextBoolean(); 
					}
				}
			),
			
//			Dieser Task läuft nur, wenn der Task davor keine Exception geworfen hat.
			new TaskImpl(
				new Callable<Integer>() {

					@Override
					public Integer call() throws Exception {
						Thread.sleep(500L);
						logger.info("task2 Callable<Integer> returning 23.");
						return 23;
					}					
				},
				
//				simuliert einen fehlerhaften Task.
				new Callable<Boolean>() {

					@Override
					public Boolean call() throws Exception {
						final Random random = new Random();
						if(!random.nextBoolean() ) {
							throw new RuntimeException("task2 Callable<Boolean>: I just felt like throwing up...");
						}
						logger.info("task2 Callable<Boolean> returning something...");
						return random.nextBoolean(); 
					}
				}
			)
		};

		new TaskSequenceImpl().runTasks(Arrays.asList(tasks));
	}
}
 
Zuletzt bearbeitet:
G

Gelöschtes Mitglied 5909

Gast
Besten Dank, hab es mittlerweile aber mit dem JobChainJobListener umgesetzt.

Klappt soweit auch ganz gut, muss nurnoch ordentlich getestet werden und so.

Da es sich um keine richtigen Runnable's handelt geht das auch nicht mit der Concurrency API (das wird von ner anderen VM abgearbeitet)
 


Schreibe deine Antwort... und nutze den </> Button, wenn du Code posten möchtest...

Ähnliche Java Themen

Neue Themen


Oben