package nl.ipo.cds.admin.ba.attributemapping;
import static nl.ipo.cds.etl.process.HarvesterMessageKey.METADATA;
import static nl.ipo.cds.etl.process.HarvesterMessageKey.METADATA_FEATURETYPE_INVALID;
import static nl.ipo.cds.etl.process.HarvesterMessageKey.METADATA_FEATURETYPE_NOT_FOUND;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import nl.ipo.cds.domain.Dataset;
import nl.ipo.cds.domain.FeatureType;
import nl.ipo.cds.etl.featuretype.FeatureTypeNotFoundException;
import nl.ipo.cds.etl.featuretype.GMLFeatureTypeParser;
import nl.ipo.cds.etl.process.DatasetMetadata;
import nl.ipo.cds.etl.process.HarvesterException;
import nl.ipo.cds.etl.process.HarvesterFactory;
import nl.ipo.cds.etl.process.MetadataHarvester;
import nl.ipo.cds.etl.theme.ThemeConfig;
import nl.ipo.cds.etl.theme.ThemeDiscoverer;
import nl.ipo.cds.etl.theme.schema.SchemaHarvester;
import org.deegree.commons.xml.XMLProcessingException;
import org.deegree.feature.types.AppSchema;
public class FeatureTypeCache {
private final HarvesterFactory harvesterFactory;
private final ThemeDiscoverer themeDiscoverer;
public FeatureTypeCache (final HarvesterFactory harvesterFactory, ThemeDiscoverer themeDiscoverer) {
this.harvesterFactory = harvesterFactory;
this.themeDiscoverer = themeDiscoverer;
}
private ConcurrentHashMap<String, CacheEntry> featureTypeCache = new ConcurrentHashMap<String, CacheEntry> ();
private static class CacheEntry {
public final Future<FeatureType> future;
public final long timestamp;
public CacheEntry (final Future<FeatureType> future, final long timestamp) {
this.future = future;
this.timestamp = timestamp;
}
}
public FeatureType getFeatureType (final Dataset dataset) throws HarvesterException {
final String uuid = dataset.getUuid ();
// Evict old entries from the cache:
final long evictTimestamp = new Date ().getTime () - 10 * 60 * 1000;
final List<Map.Entry<String, CacheEntry>> entries = new ArrayList<Map.Entry<String,CacheEntry>> (featureTypeCache.entrySet ());
Collections.sort (entries, new Comparator<Map.Entry<String, CacheEntry>> () {
@Override
public int compare (final Entry<String, CacheEntry> o1, final Entry<String, CacheEntry> o2) {
return (int)Math.signum (o1.getValue ().timestamp - o2.getValue ().timestamp);
}
});
for (int i = entries.size () - 1; i >= 0; -- i) {
if (entries.get (i).getValue ().timestamp < evictTimestamp) {
featureTypeCache.remove (entries.get (i).getKey ());
entries.remove (i);
}
}
while (entries.size () > 10) {
featureTypeCache.remove (entries.get (0).getKey ());
entries.remove (0);
}
ThemeConfig<?> themeConfig = themeDiscoverer.getThemeConfiguration(dataset.getDatasetType().getThema().getNaam());
final SchemaHarvester schemaHarvester = themeConfig.getSchemaHarvester();
// Create a task that will load the feature type if it wasn't previously cached:
final FutureTask<FeatureType> getFeatureTypeTask = new FutureTask<FeatureType> (new Callable<FeatureType> () {
@Override
public FeatureType call () throws Exception {
String schemaUrl = null;
String featureTypeName = null;
try {
MetadataHarvester harvester = harvesterFactory.createMetadataHarvester ();
DatasetMetadata metadata = harvester.parseMetadata (uuid);
if (metadata == null) {
throw new HarvesterException (METADATA, schemaUrl, featureTypeName);
}
AppSchema appSchema = schemaHarvester.parseApplicationSchema(metadata);
String ftNameFromMetadata = metadata.getFeatureTypeName();
if (ftNameFromMetadata != null) {
ftNameFromMetadata = ftNameFromMetadata.substring(ftNameFromMetadata.indexOf(':') + 1);
}
String ftLocalName = determineLocalFtName (appSchema, ftNameFromMetadata);
return new GMLFeatureTypeParser().parseSchema(appSchema, ftLocalName);
} catch (FeatureTypeNotFoundException e) {
throw new HarvesterException (e, METADATA_FEATURETYPE_NOT_FOUND, schemaUrl, featureTypeName);
} catch (XMLProcessingException e) {
throw new HarvesterException (e, METADATA_FEATURETYPE_INVALID, schemaUrl, featureTypeName, e.getMessage());
}
}
});
// Try to insert the task in the cache map. If it succeeds, run the task. Otherwise wait for the previous task
// to complete at this point:
final CacheEntry previousTask = featureTypeCache.putIfAbsent (uuid, new CacheEntry (getFeatureTypeTask, new Date ().getTime ()));
try {
if (previousTask != null) {
return previousTask.future.get ();
} else {
getFeatureTypeTask.run ();
return getFeatureTypeTask.get ();
}
} catch (ExecutionException e) {
if (e.getCause () instanceof HarvesterException) {
final HarvesterException harvesterException = (HarvesterException)e.getCause ();
throw harvesterException;
} else {
throw new RuntimeException (e);
}
} catch (InterruptedException e) {
throw new RuntimeException (e);
}
}
private String determineLocalFtName(AppSchema appSchema, String ftNameFromMetadata) throws FeatureTypeNotFoundException {
List<org.deegree.feature.types.FeatureType> fts = appSchema.getFeatureTypes(null, false, false);
if (fts.size() == 1) {
return fts.get (0).getName().getLocalPart();
}
for (org.deegree.feature.types.FeatureType ft : fts ) {
if (ft.getName().getLocalPart().equals(ftNameFromMetadata)) {
return ft.getName().getLocalPart();
}
}
throw new FeatureTypeNotFoundException(ftNameFromMetadata, appSchema);
}
}