/*******************************************************************************
* Copyright (c) 2011 Wind River Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
//#ifdef exercises
package org.eclipse.cdt.examples.dsf.dataviewer;
//#else
//#package org.eclipse.cdt.examples.dsf.dataviewer.answers;
//#endif
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.ICache;
import org.eclipse.cdt.dsf.concurrent.ImmediateInDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Transaction;
import org.eclipse.core.runtime.CoreException;
/**
* A data generator which performs a sum computation on data retrieved from a
* number of other data generators. The data retrieval from other generators
* is performed using ACPM caches and the result is calculated once all caches
* are valid.
* <p>
* Unlike {@link AsyncSumDataGenerator}, this data generator listens to events
* from the individual the data providers. Theve events are used to
* invalidate caches to make sure that they don't return incorrect data. This
* generator also sends out events to its clients to notify them to update, or
* invalidate their caches.
* </p>
*/
public class ACPMSumDataGenerator
implements IDataGenerator, IDataGenerator.Listener
{
/**
* DSF executor used to serialize data access within this data generator.
*/
final private DsfExecutor fExecutor;
/**
* Data generators to retrieve original data to perform calculations on.
* The generators are accessed through the cache manager wrappers.
*/
final private DataGeneratorCacheManager[] fDataGeneratorCMs;
/**
* List of listeners for this data generator.
*/
final private List<Listener> fListeners = new LinkedList<Listener>();
public ACPMSumDataGenerator(DsfExecutor executor,
IDataGenerator[] generators)
{
fExecutor = executor;
// Create wrappers for data generators and add ourselves as listener
// to their events.
fDataGeneratorCMs = new DataGeneratorCacheManager[generators.length];
ImmediateInDsfExecutor immediateExecutor =
new ImmediateInDsfExecutor(fExecutor);
for (int i = 0; i < generators.length; i++) {
fDataGeneratorCMs[i] = new DataGeneratorCacheManager(
immediateExecutor, generators[i]);
generators[i].addListener(this);
}
}
@Override
public void getCount(final DataRequestMonitor<Integer> rm) {
// Artificially delay the retrieval of the sum data to simulate
// real processing time.
fExecutor.schedule( new Runnable() {
@Override
public void run() {
// Create the transaction here to put all the ugly
// code in one place.
new Transaction<Integer>() {
@Override
protected Integer process()
throws Transaction.InvalidCacheException,
CoreException
{
return processCount(this);
}
}.request(rm);
}
},
PROCESSING_DELAY, TimeUnit.MILLISECONDS);
}
/**
* Perform the calculation to get the max count for the given transaction.
* @param transaction The ACPM transaction to use for calculation.
* @return Calculated count.
* @throws Transaction.InvalidCacheException {@link Transaction#process}
* @throws CoreException See {@link Transaction#process}
*/
private Integer processCount(Transaction<Integer> transaction)
throws Transaction.InvalidCacheException, CoreException
{
// Assemble all needed count caches into a collection.
List<ICache<Integer>> countCaches =
new ArrayList<ICache<Integer>>(fDataGeneratorCMs.length);
for (DataGeneratorCacheManager dataGeneratorCM : fDataGeneratorCMs) {
countCaches.add(dataGeneratorCM.getCount());
}
// Validate all count caches at once. This executes needed requests
// in parallel.
transaction.validate(countCaches);
// Calculate the max value and return.
int maxCount = 0;
for (ICache<Integer> countCache : countCaches) {
maxCount = Math.max(maxCount, countCache.getData());
}
return maxCount;
}
@Override
public void getValue(final int index, final DataRequestMonitor<Integer> rm)
{
// Add a processing delay.
fExecutor.schedule( new Runnable() {
@Override
public void run() {
new Transaction<Integer>() {
@Override
protected Integer process()
throws Transaction.InvalidCacheException,
CoreException
{
return processValue(this, index);
}
}.request(rm);
}
},
PROCESSING_DELAY, TimeUnit.MILLISECONDS);
}
/**
* Perform the calculation to get the sum of values at given index.
* @param transaction The ACPM transaction to use for calculation.
* @param index Index of value to calculate.
* @return Calculated value.
* @throws Transaction.InvalidCacheException {@link Transaction#process}
* @throws CoreException See {@link Transaction#process}
*/
private Integer processValue(Transaction<Integer> transaction, int index)
throws Transaction.InvalidCacheException, CoreException
{
List<ICache<Integer>> valueCaches =
new ArrayList<ICache<Integer>>(fDataGeneratorCMs.length);
for (DataGeneratorCacheManager dataGeneratorCM : fDataGeneratorCMs) {
valueCaches.add(dataGeneratorCM.getValue(index));
}
// Validate all value caches at once. This executes needed requests
// in parallel.
transaction.validate(valueCaches);
int sum = 0;
for (ICache<Integer> valueCache : valueCaches) {
sum += valueCache.getData();
}
return sum;
}
@Override
public void shutdown(final RequestMonitor rm) {
for (DataGeneratorCacheManager dataGeneratorCM : fDataGeneratorCMs) {
dataGeneratorCM.getDataGenerator().removeListener(this);
dataGeneratorCM.dispose();
rm.done();
}
rm.done();
}
@Override
public void addListener(final Listener listener) {
// Must access fListeners on executor thread.
try {
fExecutor.execute( new DsfRunnable() {
@Override
public void run() {
fListeners.add(listener);
}
});
} catch (RejectedExecutionException e) {}
}
@Override
public void removeListener(final Listener listener) {
// Must access fListeners on executor thread.
try {
fExecutor.execute( new DsfRunnable() {
@Override
public void run() {
fListeners.remove(listener);
}
});
} catch (RejectedExecutionException e) {}
}
@Override
public void countChanged() {
// Must access fListeners on executor thread.
try {
fExecutor.execute( new DsfRunnable() {
@Override
public void run() {
for (Listener listener : fListeners) {
listener.countChanged();
}
}
});
} catch (RejectedExecutionException e) {}
}
@Override
public void valuesChanged(final Set<Integer> changed) {
// Must access fListeners on executor thread.
try {
fExecutor.execute( new DsfRunnable() {
@Override
public void run() {
for (Object listener : fListeners) {
((Listener)listener).valuesChanged(changed);
}
}
});
} catch (RejectedExecutionException e) {}
}
}