/*
* JBoss, Home of Professional Open Source
* Copyright 2015, Red Hat, Inc. and/or its affiliates, and individual
* contributors by the @authors tag. See the copyright.txt in the
* distribution for a full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.as.quickstarts.threadracing;
import org.jboss.as.quickstarts.threadracing.results.ChampionshipStandings;
import org.jboss.as.quickstarts.threadracing.results.RaceResult;
import org.jboss.as.quickstarts.threadracing.results.RaceResults;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The awesome Java EE thread race. It's the core of the app logic, but has no usage of Java EE technologies, thus of limited interest to study.
*
* @author Eduardo Martins
*/
public class Race {
/**
* a barrier used to sync racers for the race start
*/
private CyclicBarrier startBarrier;
/**
* a count down latch used by the race to know when all racers finished/aborted
*/
private CountDownLatch endCountDownLatch;
/**
* provides the positions to racer's who finish the race, the initial value is the first position, which increments on a racer finish
*/
private AtomicInteger donePosition;
/**
* provides the position to racer's who abandon the race, the initial value is the last possible position, which decrements on a racer abort
*/
private AtomicInteger abortedPosition;
/**
* the race result
*/
private RaceResult result;
/**
* the race's environment
*/
private final Map<String, String> environment;
/**
* the race's broadcaster
*/
private final RaceBroadcaster broadcaster;
/**
* the past race results
*/
private final RaceResults results;
/**
* the racer #1
*/
private final Racer racer1;
/**
* the racer #2
*/
private final Racer racer2;
/**
* the racer #3
*/
private final Racer racer3;
/**
* the racer #4
*/
private final Racer racer4;
/**
* Creates a new race with the specified racers and environment.
* @param racer1
* @param racer2
* @param racer3
* @param racer4
* @param environment
* @param broadcaster the broadcaster that will be used to update fans about the race progress.
* @param results the race results
*/
public Race(Racer racer1, Racer racer2, Racer racer3, Racer racer4, Map<String, String> environment, RaceBroadcaster broadcaster, RaceResults results) {
this.racer1 = racer1;
this.racer2 = racer2;
this.racer3 = racer3;
this.racer4 = racer4;
this.environment = environment;
this.broadcaster = broadcaster;
this.results = results;
}
/**
* Starts the race.
* @throws Exception if there is an unexpected issue with the race, such as racers taking too long being ready to start, or not finishing the race in time.
*/
public synchronized void run() throws Exception {
reset();
broadcaster.start();
registerRacers();
startEngines();
startRace();
awaitEnd();
processResult();
}
/**
* Resets the race state.
*/
private void reset() {
startBarrier = new CyclicBarrier(5);
endCountDownLatch = new CountDownLatch(4);
donePosition = new AtomicInteger(1);
abortedPosition = new AtomicInteger(4);
result = new RaceResult();
}
/**
* Registers all racers.
*/
private void registerRacers() {
racer1.setRegistration(new Registration(racer1, 1));
racer2.setRegistration(new Registration(racer2, 2));
racer3.setRegistration(new Registration(racer3, 3));
racer4.setRegistration(new Registration(racer4, 4));
}
/**
* Starts racer's engines.
*/
private void startEngines() {
broadcaster.startYourEngines();
racer1.startEngine();
racer2.startEngine();
racer3.startEngine();
racer4.startEngine();
}
/**
* Starts the race. Note that the race only starts when all racers are ready, i.e. all waiting at the start barrier, and for that to happen there is a 30 seconds timeout.
* @throws Exception if the race start has expired
*/
private void startRace() throws Exception {
broadcaster.readySetGo();
startBarrier.await(30, TimeUnit.SECONDS);
}
/**
* Awaits the race to finish. If the race is still in progress this method will block and wait for the race to end. The wait has a timeout of 90 seconds.
* @throws Exception if the race end has expired.
*/
private void awaitEnd() throws Exception {
endCountDownLatch.await(90, TimeUnit.SECONDS);
broadcaster.raceEnd();
}
/**
* Process the race result.
*/
private void processResult() {
results.add(result);
broadcaster.raceResult(result);
broadcaster.championshipStandings(new ChampionshipStandings().addAll(results).getEntryList());
}
/**
* The racer's registration.
*/
public class Registration {
private final int number;
/**
* the registration's racer
*/
private final Racer racer;
/**
*
* @param racer
*/
private Registration(Racer racer, int number) {
this.number = number;
this.racer = racer;
broadcast("joins the race.");
}
/**
* The racer is reader to start the race.
* @throws Exception
*/
public void ready() throws Exception {
startBarrier.await(30, TimeUnit.SECONDS);
}
/**
* The racer has finished the race.
*/
public void done() {
int racerPosition = donePosition.getAndIncrement();
broadcast("finished the race.");
result.setPosition(this, racerPosition);
endCountDownLatch.countDown();
}
/**
* The racer has aborted the race.
*/
public void aborted(Throwable t) {
int racerPosition = abortedPosition.getAndDecrement();
t.printStackTrace();
broadcast("aborted the race. Reason: " + (t != null ? t.getMessage() : "N/A)"));
result.setPosition(this, racerPosition);
endCountDownLatch.countDown();
}
/**
* Retrieves the race's environment.
* @return
*/
public Map<String, String> getEnvironment() {
return environment;
}
/**
* Retrieves the registered racer.
* @return
*/
public Racer getRacer() {
return racer;
}
/**
* Retrieves the registration number.
* @return
*/
public int getNumber() {
return number;
}
/**
* Broadcasts a msg with respect to a race event.
* @param msg
*/
public void broadcast(String msg) {
broadcaster.raceProgress(racer.getName() + ' ' + msg);
}
}
}