package mil.nga.giat.geowave.core.store.memory;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.PersistenceUtils;
import mil.nga.giat.geowave.core.store.CloseableIterator;
import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics;
import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatisticsStore;
/**
* This is responsible for persisting data statistics (either in memory or to
* disk depending on the implementation).
*/
public class MemoryDataStatisticsStore implements
DataStatisticsStore
{
private final static Logger LOGGER = LoggerFactory.getLogger(MemoryDataStatisticsStore.class);
private final Map<Key, DataStatistics<?>> statsMap = new HashMap<Key, DataStatistics<?>>();
/**
* This will write the statistics to the underlying store. Note that this
* will overwrite whatever the current persisted statistics are with the
* given statistics ID and data adapter ID. Use incorporateStatistics to
* aggregate the statistics with any existing statistics.
*
* @param statistics
* The statistics to write
*
*/
@Override
public void setStatistics(
final DataStatistics<?> statistics ) {
statsMap.put(
new Key(
statistics.getDataAdapterId(),
statistics.getStatisticsId(),
statistics.getVisibility()),
statistics);
}
/**
* Add the statistics to the store, overwriting existing data statistics
* with the aggregation of these statistics and the existing statistics
*
* @param statistics
* the data statistics
*/
@Override
public void incorporateStatistics(
final DataStatistics<?> statistics ) {
final Key key = new Key(
statistics.getDataAdapterId(),
statistics.getStatisticsId(),
statistics.getVisibility());
DataStatistics<?> existingStats = statsMap.get(key);
if (existingStats == null) {
statsMap.put(
key,
statistics);
}
else {
existingStats = PersistenceUtils.fromBinary(
PersistenceUtils.toBinary(existingStats),
DataStatistics.class);
existingStats.merge(statistics);
statsMap.put(
key,
existingStats);
}
}
/**
* Get all data statistics from the store by a data adapter ID
*
* @param adapterId
* the data adapter ID
* @return the list of statistics for the given adapter, empty if it doesn't
* exist
*/
@Override
public CloseableIterator<DataStatistics<?>> getDataStatistics(
final ByteArrayId adapterId,
final String... authorizations ) {
final List<DataStatistics<?>> statSet = new ArrayList<DataStatistics<?>>();
for (final DataStatistics<?> stat : statsMap.values()) {
if (stat.getDataAdapterId().equals(
adapterId)) {
statSet.add(stat);
}
}
return new CloseableIterator.Wrapper<DataStatistics<?>>(
statSet.iterator());
}
/**
* Get all data statistics from the store
*
* @return the list of all statistics
*/
@Override
public CloseableIterator<DataStatistics<?>> getAllDataStatistics(
final String... authorizations ) {
return new CloseableIterator.Wrapper<DataStatistics<?>>(
statsMap.values().iterator());
}
/**
* Get statistics by adapter ID and the statistics ID (which will define a
* unique statistic)
*
* @param adapterId
* The adapter ID for the requested statistics
* @param statisticsId
* the statistics ID for the requested statistics
* @return the persisted statistics value
*/
@Override
public DataStatistics<?> getDataStatistics(
final ByteArrayId adapterId,
final ByteArrayId statisticsId,
final String... authorizations ) {
final List<DataStatistics<?>> statSet = new ArrayList<DataStatistics<?>>();
for (final DataStatistics<?> stat : statsMap.values()) {
if (stat.getDataAdapterId().equals(
adapterId) && stat.getStatisticsId().equals(
statisticsId) && MemoryStoreUtils.isAuthorized(
stat.getVisibility(),
authorizations)) {
statSet.add(stat);
}
}
return (statSet.size()) > 0 ? statSet.get(0) : null;
}
/**
* Remove a statistic from the store
*
* @param adapterId
* @param statisticsId
* @return a flag indicating whether a statistic had existed with the given
* IDs and was successfully deleted.
*/
@Override
public boolean removeStatistics(
final ByteArrayId adapterId,
final ByteArrayId statisticsId,
final String... authorizations ) {
final List<DataStatistics<?>> statSet = new ArrayList<DataStatistics<?>>();
for (final DataStatistics<?> stat : statsMap.values()) {
if (stat.getDataAdapterId().equals(
adapterId) && stat.getStatisticsId().equals(
statisticsId) && MemoryStoreUtils.isAuthorized(
stat.getVisibility(),
authorizations)) {
statSet.add(stat);
}
}
if (statSet.size() > 0) {
final DataStatistics<?> statistics = statSet.get(0);
statsMap.remove(new Key(
statistics.getDataAdapterId(),
statistics.getStatisticsId(),
statistics.getVisibility()));
return true;
}
return false;
}
@Override
public void removeAll() {
statsMap.clear();
}
private static class Key
{
ByteArrayId adapterId;
ByteArrayId statisticsId;
byte[] authorizations;
public Key(
final ByteArrayId adapterId,
final ByteArrayId statisticsId,
final byte[] authorizations ) {
super();
this.adapterId = adapterId;
this.statisticsId = statisticsId;
this.authorizations = authorizations;
}
public ByteArrayId getAdapterId() {
return adapterId;
}
public void setAdapterId(
final ByteArrayId adapterId ) {
this.adapterId = adapterId;
}
public ByteArrayId getStatisticsId() {
return statisticsId;
}
public void setStatisticsId(
final ByteArrayId statisticsId ) {
this.statisticsId = statisticsId;
}
public byte[] getAuthorizations() {
return authorizations;
}
public void setAuthorizations(
final byte[] authorizations ) {
this.authorizations = authorizations;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((adapterId == null) ? 0 : adapterId.hashCode());
result = (prime * result) + Arrays.hashCode(authorizations);
result = (prime * result) + ((statisticsId == null) ? 0 : statisticsId.hashCode());
return result;
}
@Override
public boolean equals(
final Object obj ) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Key other = (Key) obj;
if (adapterId == null) {
if (other.adapterId != null) {
return false;
}
}
else if (!adapterId.equals(other.adapterId)) {
return false;
}
if (!Arrays.equals(
authorizations,
other.authorizations)) {
return false;
}
if (statisticsId == null) {
if (other.statisticsId != null) {
return false;
}
}
else if (!statisticsId.equals(other.statisticsId)) {
return false;
}
return true;
}
}
@Override
public void removeAllStatistics(
final ByteArrayId adapterId,
final String... authorizations ) {
final Iterator<Entry<Key, DataStatistics<?>>> it = statsMap.entrySet().iterator();
while (it.hasNext()) {
final Entry<Key, DataStatistics<?>> entry = it.next();
if (entry.getKey().adapterId.equals(adapterId) && MemoryStoreUtils.isAuthorized(
entry.getKey().authorizations,
authorizations)) {
it.remove();
}
}
}
@Override
public void transformVisibility(
final ByteArrayId adapterId,
final String transformingRegex,
final String replacement,
final String... authorizations ) {
final Pattern pattern = Pattern.compile(transformingRegex);
final Iterator<Entry<Key, DataStatistics<?>>> it = statsMap.entrySet().iterator();
final List<DataStatistics<?>> newStats = new ArrayList<DataStatistics<?>>();
while (it.hasNext()) {
final Entry<Key, DataStatistics<?>> entry = it.next();
String visibility = null;
try {
visibility = new String(
entry.getValue().getVisibility(),
"UTF-8");
}
catch (final UnsupportedEncodingException e) {
LOGGER.error(
"Invalid visibility " + Arrays.toString(entry.getValue().getVisibility()),
e);
continue;
}
if (entry.getKey().adapterId.equals(adapterId) && MemoryStoreUtils.isAuthorized(
entry.getKey().authorizations,
authorizations) && pattern.matcher(
visibility).find()) {
String newVisibility = visibility.replaceFirst(
transformingRegex,
replacement);
if (newVisibility.length() > 0) {
final char one = newVisibility.charAt(0);
// strip off any ending options
if ((one == '&') || (one == '|')) {
newVisibility = newVisibility.substring(1);
}
}
try {
entry.getValue().setVisibility(
newVisibility.getBytes("UTF-8"));
}
catch (final UnsupportedEncodingException e) {
LOGGER.error(
"Invalid visibility " + newVisibility,
e);
continue;
}
newStats.add(entry.getValue());
it.remove();
}
}
for (final DataStatistics<?> entry : newStats) {
incorporateStatistics(entry);
}
}
}