package nl.fontys.sofa.limo.simulation.task; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import nl.fontys.sofa.limo.domain.component.SupplyChain; import nl.fontys.sofa.limo.simulation.SimulationExecutor; import nl.fontys.sofa.limo.simulation.result.SimulationResult; import org.netbeans.api.progress.aggregate.AggregateProgressFactory; import org.netbeans.api.progress.aggregate.ProgressContributor; import org.openide.util.Cancellable; import org.openide.util.Exceptions; import org.openide.util.RequestProcessor.Task; import org.openide.util.TaskListener; /** * * @author Dominik Kaisers {@literal <d.kaisers@student.fontys.nl>} */ public class Simulation implements Runnable, TaskListener, Cancellable { private final AtomicBoolean running; private final SupplyChain supplyChain; private final Map<org.openide.util.Task, TestCase> testCaseTasks; private final int testCaseCount; private final SimulationResult result; private AtomicInteger finishedCount; private final BlockingQueue<SupplyChain> scPool; private final ProgressContributor progressContributor; private final Object pcLock = new Object(); public Simulation(SupplyChain supplyChain, int testCaseCount, String id) { this.supplyChain = supplyChain; this.testCaseCount = testCaseCount; this.testCaseTasks = new ConcurrentHashMap<>(); this.result = new SimulationResult(supplyChain); this.finishedCount = new AtomicInteger(0); this.scPool = new LinkedBlockingQueue<>(); this.progressContributor = AggregateProgressFactory.createProgressContributor(id); this.running = new AtomicBoolean(false); } public boolean isDone() { return finishedCount.get() == testCaseCount; } /** * Get the percentage of finished test cases. * * @return Percentage of test cases finished. */ public double getProgress() { return isDone() ? 1.0d : (finishedCount.doubleValue() / (double) testCaseCount); } public SimulationResult getResult() { return result; } public ProgressContributor getProgressContributor() { return progressContributor; } /** * Deep copies a supply chain using in memory serialization. * * @param supplyChain Supply chain to copy. * @return Copied supply chain. * @throws IOException * @throws ClassNotFoundException */ private SupplyChain deepCopy(SupplyChain supplyChain) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(supplyChain); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (SupplyChain) ois.readObject(); } @Override public void run() { running.set(true); testCaseTasks.clear(); progressContributor.start(testCaseCount); finishedCount = new AtomicInteger(0); // Create supply chain pool for (int i = 0; i < 5; i++) { try { SupplyChain copy = deepCopy(supplyChain); scPool.offer(copy); } catch (IOException | ClassNotFoundException ex) { Exceptions.printStackTrace(ex); } } if (scPool.isEmpty()) { return; } int i = 0; while (i < testCaseCount && running.get()) { SupplyChain sc; synchronized (scPool) { while (scPool.isEmpty()) { try { scPool.wait(); } catch (InterruptedException ex) { // OK Keep waiting System.out.println("Simulation thread interrupted while waiting for supply chain. Keep waiting."); } } sc = scPool.poll(); if (sc == null) { continue; } } // Submit test cases and attach this as listener TestCase testCase = new TestCase(sc); Task task = SimulationExecutor.post(testCase); task.addTaskListener(this); // Put into map testCaseTasks.put(task, testCase); i++; task.schedule(0); } } @Override public void taskFinished(org.openide.util.Task task) { finishedCount.incrementAndGet(); TestCase tc = testCaseTasks.remove(task); // Check for null tc result.addTestCaseResult(tc.getResult()); synchronized (scPool) { scPool.offer(tc.getResult().getSupplyChain()); scPool.notify(); } synchronized (pcLock) { if (finishedCount.get() % 1000 == 0) { progressContributor.progress(finishedCount.get() + " of " + testCaseCount + " test cases run.", finishedCount.get()); } if (isDone()) { running.set(false); progressContributor.finish(); } } } @Override public boolean cancel() { running.set(false); return true; } }