package gedi.solutions.geode.operations.stats;

import static gedi.solutions.geode.operations.stats.StatArchiveFormat.ARCHIVE_VERSION;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.BOOLEAN_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.BYTE_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.CHAR_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.COMPACT_VALUE_2_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.DOUBLE_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.FLOAT_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.HEADER_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.ILLEGAL_RESOURCE_INST_ID;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.ILLEGAL_RESOURCE_INST_ID_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.ILLEGAL_STAT_OFFSET;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.INT_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.INT_TIMESTAMP_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.LONG_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.MAX_BYTE_RESOURCE_INST_ID;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.MIN_1BYTE_COMPACT_VALUE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.RESOURCE_INSTANCE_CREATE_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.RESOURCE_INSTANCE_DELETE_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.RESOURCE_INSTANCE_INITIALIZE_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.RESOURCE_TYPE_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.SAMPLE_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.SHORT_CODE;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.SHORT_RESOURCE_INST_ID_TOKEN;
import static gedi.solutions.geode.operations.stats.StatArchiveFormat.WCHAR_CODE;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;

import gedi.solutions.geode.operations.stats.visitors.GenericCsvStatsVisitor;
import gedi.solutions.geode.operations.stats.visitors.StatsVisitor;
import nyla.solutions.core.io.IO;

/**
 * Based on GEODE-78
 *
 * @author Gregory Green
 *
 */
public class GfStatsReader implements StatsInfo
{

	private static final String GEMFIRE_LOG_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS z";

	protected static final NumberFormat nf = NumberFormat.getNumberInstance();

	static
	{
		nf.setMaximumFractionDigits(2);
		nf.setGroupingUsed(false);
	}

	private InputStream is;
	private DataInputStream dataIn;
	private ValueFilter[] filters;
	private final File archive;
	private /* final */ int archiveVersion;
	private /* final */ ArchiveInfo info;
	private final boolean compressed;
	private boolean updateOK;
	private final boolean dump;
	private boolean closed = false;
	protected int resourceInstSize = 0;
	protected ResourceInst[] resourceInstTable = null;
	private ResourceType[] resourceTypeTable = null;
	private final TimeStampSeries timeSeries = new TimeStampSeries();
	private final DateFormat timeFormatter = new SimpleDateFormat(GEMFIRE_LOG_FORMAT);
	private final static int BUFFER_SIZE = 1024 * 1024;
	private final ArrayList<ComboValue> fileComboValues = new ArrayList<ComboValue>();

	public GfStatsReader(String archiveName) throws IOException
	{
		this(archiveName, false, null);
	}

	public String getFileName()
	{
		return archive.getName();
	}// ------------------------------------------------

	public GfStatsReader(String archiveName, boolean dump, ValueFilter[] filters) throws IOException
	{
		this.archive = new File(archiveName);
		this.dump = dump;
		this.compressed = archive.getPath().endsWith(".gz");
		this.is = new FileInputStream(this.archive);
		if (this.compressed)
		{
			this.dataIn = new DataInputStream(
					new BufferedInputStream(new GZIPInputStream(this.is, BUFFER_SIZE), BUFFER_SIZE));
		}
		else
		{
			this.dataIn = new DataInputStream(new BufferedInputStream(this.is, BUFFER_SIZE));
		}
		this.updateOK = this.dataIn.markSupported();
		this.filters = createFilters(filters);

		update(false);
	}

	protected String getArchiveFileName()
	{
		return archive.getAbsolutePath();
	}

	public List<ResourceInst> getResourceInstList()
	{
		List<ResourceInst> result = new ArrayList<>();
		for (ResourceInst r : resourceInstTable)
		{
			if (r != null)
			{
				result.add(r);
			}
		}
		return result;
	}

	public List<ResourceType> getResourceTypeList()
	{
		List<ResourceType> result = new ArrayList<>();
		for (ResourceType t : resourceTypeTable)
		{
			if (t != null)
			{
				result.add(t);
			}
		}
		return result;
	}

	private ValueFilter[] createFilters(ValueFilter[] allFilters)
	{
		if (allFilters == null)
		{
			return new ValueFilter[0];
		}
		ArrayList<ValueFilter> l = new ArrayList<>();
		for (ValueFilter allFilter : allFilters)
		{
			if (allFilter.archiveMatches(archive))
			{
				l.add(allFilter);
			}
		}
		if (l.size() == allFilters.length)
		{
			return allFilters;
		}
		else
		{
			ValueFilter[] result = new ValueFilter[l.size()];
			return l.toArray(result);
		}
	}

	void matchSpec(StatSpec spec, List<StatValue> matchedValues)
	{
		if (spec.getCombineType() == StatSpec.FILE)
		{
			// search for previous ComboValue
			for (Object fileComboValue : this.fileComboValues)
			{
				ComboValue v = (ComboValue) fileComboValue;
				if (!spec.statMatches(v.getDescriptor().getName()))
				{
					continue;
				}
				if (!spec.typeMatches(v.getType().getName()))
				{
					continue;
				}
				ResourceInst[] resources = v.getResources();
				for (ResourceInst resource : resources)
				{
					if (!spec.instanceMatches(resource.getName(), resource.getId()))
					{
					}
					// note: we already know the archive file matches
				}
				matchedValues.add(v);
				return;
			}
			ArrayList<StatValue> l = new ArrayList<StatValue>();
			matchSpec(new RawStatSpec(spec), l);
			if (l.size() != 0)
			{
				ComboValue cv = new ComboValue(l);
				// save this in file's combo value list
				this.fileComboValues.add(cv);
				matchedValues.add(cv);
			}
		}
		else
		{
			for (int instIdx = 0; instIdx < resourceInstSize; instIdx++)
			{
				resourceInstTable[instIdx].matchSpec(spec, matchedValues);
			}
		}
	}

	/**
	 * Formats an archive timestamp in way consistent with GemFire log dates. It
	 * will also be formatted to reflect the time zone the archive was created
	 * in.
	 *
	 * @param ts
	 *            The difference, measured in milliseconds, between the time
	 *            marked by this time stamp and midnight, January 1, 1970 UTC.
	 *  @return format yyyy/MM/dd HH:mm:ss.SSS z
	 */
	public String formatTimeMillis(long ts)
	{
		synchronized (timeFormatter)
		{
			return timeFormatter.format(new Date(ts));
		}
	}

	/**
	 * sets the time zone this archive was written in.
	 */
	void setTimeZone(TimeZone z)
	{
		timeFormatter.setTimeZone(z);
	}

	/**
	 * @return the time series for this archive.
	 */
	TimeStampSeries getTimeStamps()
	{
		return timeSeries;
	}

	/**
	 * Checks to see if the archive has changed since the StatArchiverReader
	 * instance was created or last updated. If the archive has additional
	 * samples then those are read the resource instances maintained by the
	 * reader are updated.
	 *

	 * Once closed a reader can no longer be updated.
	 *
	 *@param doReset update flag
	 * @return true if update read some new data.
	 * @throws java.io.IOException
	 *             if archiveName could not be opened read, or
	 *             closed.
	 */
	public boolean update(boolean doReset) throws IOException
	{
		if (this.closed)
		{
			return false;
		}
		if (!this.updateOK)
		{
			throw new RuntimeException("Update of this type of file is not supported");
		}

		if (doReset)
		{
			this.dataIn.reset();
		}

		int updateTokenCount = 0;
		while (this.readToken())
		{
			updateTokenCount++;
		}
		return updateTokenCount != 0;
	}//------------------------------------------------

	/**
	 * Print to standard out
	 */
	public void dump()
	{
		try(PrintWriter pw = new PrintWriter(System.out))
		{
			dump(pw);
		}
	}//------------------------------------------------

	public void dumpCsvFiles()
	{
		GenericCsvStatsVisitor visitor = new GenericCsvStatsVisitor(this.archive);
		this.accept(visitor);
	}
	/**
	 *
	 * @param stream the stream to print output
	 */
	public void dump(PrintWriter stream)
	{
		stream.print("archive=" + archive);
		if (info != null)
		{
			info.dump(stream);
		}
		for (ResourceType aResourceTypeTable : resourceTypeTable)
		{
			if (aResourceTypeTable != null)
			{
				aResourceTypeTable.dump(stream);
			}
		}
		stream.print("time=");
		timeSeries.dump(stream);
		for (ResourceInst inst : resourceInstTable)
		{
			if (inst != null)
			{
				inst.dump(stream);
			}
		}
	}

	/**
	 * Closes the archive.
	 * @throws IOException when an IO error occurs
	 */
	public void close() throws IOException
	{
		if (!this.closed)
		{
			this.closed = true;
			this.is.close();
			this.dataIn.close();
			this.is = null;
			this.dataIn = null;
			int typeCount = 0;
			if (this.resourceTypeTable != null)
			{ // fix for bug 32320
				for (int i = 0; i < this.resourceTypeTable.length; i++)
				{
					if (this.resourceTypeTable[i] != null)
					{
						if (this.resourceTypeTable[i].close())
						{
							this.resourceTypeTable[i] = null;
						}
						else
						{
							typeCount++;
						}
					}
				}
				ResourceType[] newTypeTable = new ResourceType[typeCount];
				typeCount = 0;
				for (ResourceType aResourceTypeTable : this.resourceTypeTable)
				{
					if (aResourceTypeTable != null)
					{
						newTypeTable[typeCount] = aResourceTypeTable;
						typeCount++;
					}
				}
				this.resourceTypeTable = newTypeTable;
			}

			if (this.resourceInstTable != null)
			{ // fix for bug 32320
				int instCount = 0;
				for (int i = 0; i < this.resourceInstTable.length; i++)
				{
					if (this.resourceInstTable[i] != null)
					{
						if (this.resourceInstTable[i].close())
						{
							this.resourceInstTable[i] = null;
						}
						else
						{
							instCount++;
						}
					}
				}
				ResourceInst[] newInstTable = new ResourceInst[instCount];
				instCount = 0;
				for (ResourceInst aResourceInstTable : this.resourceInstTable)
				{
					if (aResourceInstTable != null)
					{
						newInstTable[instCount] = aResourceInstTable;
						instCount++;
					}
				}
				this.resourceInstTable = newInstTable;
				this.resourceInstSize = instCount;
			}
			// optimize memory usage of timeSeries now that no more samples
			this.timeSeries.shrink();
			// filters are no longer needed since file will not be read from
			this.filters = null;
		}
	}

	/**
	 * @return global information about the read archive. @return null if no
	 * information is available.
	 */
	public ArchiveInfo getArchiveInfo()
	{
		return this.info;
	}

	private void readHeaderToken() throws IOException
	{
		byte archiveVersion = dataIn.readByte();
		long startTimeStamp = dataIn.readLong();
		long systemId = dataIn.readLong();
		long systemStartTimeStamp = dataIn.readLong();
		int timeZoneOffset = dataIn.readInt();
		String timeZoneName = dataIn.readUTF();
		String systemDirectory = dataIn.readUTF();
		String productVersion = dataIn.readUTF();
		String os = dataIn.readUTF();
		String machine = dataIn.readUTF();
		if (archiveVersion <= 1)
		{
			throw new RuntimeException("Archive version " + archiveVersion + " is no longer supported");
		}
		if (archiveVersion > ARCHIVE_VERSION)
		{
			throw new RuntimeException(
					"Unsupported archive version " + archiveVersion + ". The supported version is " + ARCHIVE_VERSION);
		}
		this.archiveVersion = archiveVersion;
		this.info = new ArchiveInfo(this, archiveVersion, startTimeStamp, systemStartTimeStamp, timeZoneOffset,
				timeZoneName, systemDirectory, systemId, productVersion, os, machine);
		// Clear all previously read types and instances
		this.resourceInstSize = 0;
		this.resourceInstTable = new ResourceInst[1024];
		this.resourceTypeTable = new ResourceType[256];
		timeSeries.setBase(startTimeStamp);
		if (dump)
		{
			info.dump(new PrintWriter(System.out));
		}
	}

	boolean loadType(String typeName)
	{
		// note we don't have instance data or descriptor data yet
		if (filters == null || filters.length == 0)
		{
			return true;
		}
		else
		{
			for (ValueFilter filter : filters)
			{
				if (filter.typeMatches(typeName))
				{
					return true;
				}
			}
			// System.out.println("DEBUG: don't load type=" + typeName);
			return false;
		}
	}

	boolean loadStatDescriptor(StatDescriptor stat, ResourceType type)
	{
		// note we don't have instance data yet
		if (!type.isLoaded())
		{
			return false;
		}
		if (filters == null || filters.length == 0)
		{
			return true;
		}
		else
		{
			for (ValueFilter filter : filters)
			{
				if (filter.statMatches(stat.getName()) && filter.typeMatches(type.getName()))
				{
					return true;
				}
			}
			// System.out.println("DEBUG: don't load stat=" + stat.getName());
			stat.unload();
			return false;
		}
	}

	boolean loadInstance(String textId, long numericId, ResourceType type)
	{
		if (!type.isLoaded())
		{
			return false;
		}
		if (filters == null || filters.length == 0)
		{
			return true;
		}
		else
		{
			for (ValueFilter filter : filters)
			{
				if (filter.typeMatches(type.getName()))
				{
					if (filter.instanceMatches(textId, numericId))
					{
						StatDescriptor[] stats = type.getStats();
						for (int j = 0; j < stats.length; j++)
						{
							if (stats[j].isLoaded())
							{
								if (filter.statMatches(stats[j].getName()))
								{
									return true;
								}
							}
						}
					}
				}
			}
			// System.out.println("DEBUG: don't load instance=" + textId);
			// type.unload();
			return false;
		}
	}

	boolean loadStat(StatDescriptor stat, ResourceInst resource)
	{
		ResourceType type = resource.getType();
		if (!resource.isLoaded() || !type.isLoaded() || !stat.isLoaded())
		{
			return false;
		}
		if (filters == null || filters.length == 0)
		{
			return true;
		}
		else
		{
			String textId = resource.getName();
			long numericId = resource.getId();
			for (ValueFilter filter : filters)
			{
				if (filter.statMatches(stat.getName()) && filter.typeMatches(type.getName())
						&& filter.instanceMatches(textId, numericId))
				{
					return true;
				}
			}
			return false;
		}
	}

	private void readResourceTypeToken() throws IOException
	{
		int resourceTypeId = dataIn.readInt();
		String resourceTypeName = dataIn.readUTF();
		String resourceTypeDesc = dataIn.readUTF();
		int statCount = dataIn.readUnsignedShort();
		while (resourceTypeId >= resourceTypeTable.length)
		{
			ResourceType[] tmp = new ResourceType[resourceTypeTable.length + 128];
			System.arraycopy(resourceTypeTable, 0, tmp, 0, resourceTypeTable.length);
			resourceTypeTable = tmp;
		}
		assert (resourceTypeTable[resourceTypeId] == null);

		ResourceType rt;
		if (loadType(resourceTypeName))
		{
			rt = new ResourceType(resourceTypeName, resourceTypeDesc, statCount);
			if (dump)
			{
				System.out.println("ResourceType id=" + resourceTypeId + " name=" + resourceTypeName + " statCount="
						+ statCount + " desc=" + resourceTypeDesc);
			}
		}
		else
		{
			rt = new ResourceType(resourceTypeName, statCount);
			if (dump)
			{
				System.out.println("Not loading ResourceType id=" + resourceTypeId + " name=" + resourceTypeName);
			}
		}
		resourceTypeTable[resourceTypeId] = rt;
		for (int i = 0; i < statCount; i++)
		{
			String statName = dataIn.readUTF();
			byte typeCode = dataIn.readByte();
			boolean isCounter = dataIn.readBoolean();
			boolean largerBetter = isCounter; // default
			if (this.archiveVersion >= 4)
			{
				largerBetter = dataIn.readBoolean();
			}
			String units = dataIn.readUTF();
			String desc = dataIn.readUTF();
			rt.addStatDescriptor(this, i, statName, isCounter, largerBetter, typeCode, units, desc);
			if (dump)
			{
				System.out.println("  " + i + "=" + statName + " isCtr=" + isCounter + " largerBetter=" + largerBetter
						+ " typeCode=" + typeCode + " units=" + units + " desc=" + desc);
			}
		}
	}

	private void readResourceInstanceCreateToken(boolean initialize) throws IOException
	{
		int resourceInstId = dataIn.readInt();
		String name = dataIn.readUTF();
		long id = dataIn.readLong();
		int resourceTypeId = dataIn.readInt();
		while (resourceInstId >= resourceInstTable.length)
		{
			ResourceInst[] tmp = new ResourceInst[resourceInstTable.length + 128];
			System.arraycopy(resourceInstTable, 0, tmp, 0, resourceInstTable.length);
			resourceInstTable = tmp;
		}
		assert (resourceInstTable[resourceInstId] == null);
		if ((resourceInstId + 1) > this.resourceInstSize)
		{
			this.resourceInstSize = resourceInstId + 1;
		}
		boolean loadInstance = loadInstance(name, id, resourceTypeTable[resourceTypeId]);
		resourceInstTable[resourceInstId] = new ResourceInst(this, name, id, resourceTypeTable[resourceTypeId],
				loadInstance);
		if (dump)
		{
			System.out.println((loadInstance ? "Loaded" : "Did not load") + " resource instance " + resourceInstId);
			System.out.println("  name=" + name + " id=" + id + " typeId=" + resourceTypeId);
		}
		if (initialize)
		{
			StatDescriptor[] stats = resourceInstTable[resourceInstId].getType().getStats();
			for (int i = 0; i < stats.length; i++)
			{
				long v;
				switch (stats[i].getTypeCode())
				{
				case BOOLEAN_CODE:
				case BYTE_CODE:
				case CHAR_CODE:
					v = dataIn.readByte();
					break;
				case WCHAR_CODE:
					v = dataIn.readUnsignedShort();
					break;
				case SHORT_CODE:
					v = dataIn.readShort();
					break;
				case INT_CODE:
				case FLOAT_CODE:
				case LONG_CODE:
				case DOUBLE_CODE:
					v = readCompactValue();
					break;
				default:
					throw new IOException("Unexpected typecode value" + stats[i].getTypeCode());
				}
				resourceInstTable[resourceInstId].initialValue(i, v);
			}
		}
	}

	private void readResourceInstanceDeleteToken() throws IOException
	{
		int resourceInstId = dataIn.readInt();
		assert (resourceInstTable[resourceInstId] != null);
		resourceInstTable[resourceInstId].makeInactive();
		if (dump)
		{
			System.out.println("Delete resource instance " + resourceInstId);
		}
	}

	private int readResourceInstId() throws IOException
	{
		/*
		 * if (this.archiveVersion <= 1) { return dataIn.readInt(); }
		 */
		int token = dataIn.readUnsignedByte();
		if (token <= MAX_BYTE_RESOURCE_INST_ID)
		{
			return token;
		}
		else if (token == ILLEGAL_RESOURCE_INST_ID_TOKEN)
		{
			return ILLEGAL_RESOURCE_INST_ID;
		}
		else if (token == SHORT_RESOURCE_INST_ID_TOKEN)
		{
			return dataIn.readUnsignedShort();
		}
		else
		{ /* token == INT_RESOURCE_INST_ID_TOKEN */
			return dataIn.readInt();
		}
	}

	private int readTimeDelta() throws IOException
	{
		int result = dataIn.readUnsignedShort();
		if (result == INT_TIMESTAMP_TOKEN)
		{
			result = dataIn.readInt();
		}
		return result;
	}

	private long readCompactValue() throws IOException
	{
		long v = dataIn.readByte();

		if (v < MIN_1BYTE_COMPACT_VALUE)
		{
			if (v == COMPACT_VALUE_2_TOKEN)
			{
				v = dataIn.readShort();
			}
			else
			{
				int bytesToRead = ((byte) v - COMPACT_VALUE_2_TOKEN) + 2;
				v = dataIn.readByte(); // note the first byte will be a signed
										// byte.

				bytesToRead--;
				while (bytesToRead > 0)
				{
					v <<= 8;
					v |= dataIn.readUnsignedByte();
					bytesToRead--;
				}
			}
		}
		return v;
	}

	private void readSampleToken() throws IOException
	{
		int millisSinceLastSample = readTimeDelta();
		if (dump)
		{
			System.out.println("ts=" + millisSinceLastSample);
		}
		int resourceInstId = readResourceInstId();
		while (resourceInstId != ILLEGAL_RESOURCE_INST_ID)
		{
			if (dump)
			{
				System.out.print("  instId=" + resourceInstId);
			}
			StatDescriptor[] stats = resourceInstTable[resourceInstId].getType().getStats();
			int statOffset = dataIn.readUnsignedByte();
			while (statOffset != ILLEGAL_STAT_OFFSET)
			{
				long statDeltaBits;
				switch (stats[statOffset].getTypeCode())
				{
				case BOOLEAN_CODE:
				case BYTE_CODE:
				case CHAR_CODE:
					statDeltaBits = dataIn.readByte();
					break;
				case WCHAR_CODE:
					statDeltaBits = dataIn.readUnsignedShort();
					break;
				case SHORT_CODE:
					statDeltaBits = dataIn.readShort();
					break;
				case INT_CODE:
				case FLOAT_CODE:
				case LONG_CODE:
				case DOUBLE_CODE:
					statDeltaBits = readCompactValue();
					break;
				default:
					throw new IOException("Unexepcted typecode value " + stats[statOffset].getTypeCode());
				}
				if (resourceInstTable[resourceInstId].addValueSample(statOffset, statDeltaBits))
				{
					if (dump)
					{
						System.out.print(" [" + statOffset + "]=" + statDeltaBits);
					}
				}
				statOffset = dataIn.readUnsignedByte();
			}
			if (dump)
			{
				System.out.println();
			}
			resourceInstId = readResourceInstId();
		}
		timeSeries.addTimeStamp(millisSinceLastSample);
		for (ResourceInst inst : resourceInstTable)
		{
			if (inst != null && inst.isActive())
			{
				inst.addTimeStamp();
			}
		}
	}

	/**
	 * @return true if token read, false if eof.
	 */
	private boolean readToken() throws IOException
	{
		byte token;
		try
		{
			if (this.updateOK)
			{
				this.dataIn.mark(BUFFER_SIZE);
			}
			token = this.dataIn.readByte();
			switch (token)
			{
			case HEADER_TOKEN:
				readHeaderToken();
				break;
			case RESOURCE_TYPE_TOKEN:
				readResourceTypeToken();
				break;
			case RESOURCE_INSTANCE_CREATE_TOKEN:
				readResourceInstanceCreateToken(false);
				break;
			case RESOURCE_INSTANCE_INITIALIZE_TOKEN:
				readResourceInstanceCreateToken(true);
				break;
			case RESOURCE_INSTANCE_DELETE_TOKEN:
				readResourceInstanceDeleteToken();
				break;
			case SAMPLE_TOKEN:
				readSampleToken();
				break;
			default:
				throw new IOException("Unexpected token byte value " + token);
			}
			return true;
		}
		catch (EOFException ignore)
		{
			return false;
		}
	}

	/**
	 * @return the approximate amount of memory used to implement this object.
	 */
	protected int getMemoryUsed()
	{
		int result = 0;
		for (int i = 0; i < resourceInstTable.length; i++)
		{
			if (resourceInstTable[i] != null)
			{
				result += resourceInstTable[i].getMemoryUsed();
			}
		}
		return result;
	}

	protected static double bitsToDouble(int type, long bits)
	{
		switch (type)
		{
		case BOOLEAN_CODE:
		case BYTE_CODE:
		case CHAR_CODE:
		case WCHAR_CODE:
		case SHORT_CODE:
		case INT_CODE:
		case LONG_CODE:
			return bits;
		case FLOAT_CODE:
			return Float.intBitsToFloat((int) bits);
		case DOUBLE_CODE:
			return Double.longBitsToDouble(bits);
		default:
			throw new RuntimeException("Unexpected typecode: " + type);
		}
	}

	@Override
	public void accept(StatsVisitor visitor)
	{

		if (info != null)
		{
			visitor.visitArchInfo(info);
		}

		visitor.visitResourceInsts(resourceInstTable);

		if(resourceTypeTable != null)
		{
			for (ResourceType aResourceTypeTable : resourceTypeTable)
			{
				if (aResourceTypeTable != null)
				{
					visitor.visitResourceType(aResourceTypeTable);
				}
			}
		}

		visitor.visitTimeStampSeries(timeSeries);

		if(resourceInstTable != null)
		{
			for (ResourceInst inst : resourceInstTable)
			{
				if (inst != null)
				{
					visitor.visitResourceInst(inst);
				}
			}

		}

	}//------------------------------------------------
	/**
	 * Finds and converts all statistics files in a given directory to CSV
	 * @param directory the top directory
	 * @throws IOException when an IO issues occurs
	 */
	public static void toCvsFiles(File directory)
	throws IOException
	{

		Set<File> statsFiles = IO.listFileRecursive(directory, "*.gfs");

		if(statsFiles == null || statsFiles.isEmpty())
			return;

		for (File archiveFile : statsFiles)
		{
			GfStatsReader reader = new GfStatsReader(archiveFile.getAbsolutePath());
			reader.dumpCsvFiles();
		}

	}//------------------------------------------------
	/**
	 * Main method to extract GF Stats to file
	 * @param args archiveFile csvFile [statName ]*
	 */
	public static void main(String[] args)
	{
		File archiveFile, csvFile;

		if(args.length < 1)
		{
			System.err.println("Usage: java "+GfStatsReader.class.getName()+" archiveFile [csvFile [statName ]*]");
			return;
		}
		try
		{
			archiveFile = Paths.get(args[0]).toFile();

			if(archiveFile.isDirectory())
			{
				toCvsFiles(archiveFile);
				return;
			}

			if(args.length < 2)
			{
				GfStatsReader reader = new GfStatsReader(archiveFile.getAbsolutePath());
				reader.dumpCsvFiles();
				return;
			}

			String typeName = args[1];

			csvFile = Paths.get(args[2]).toFile();

			GenericCsvStatsVisitor visitor = null;

			if(args.length > 3)
			{
				String[] stateNames = Arrays.copyOfRange(args, 2, args.length-1);
				visitor = new GenericCsvStatsVisitor(csvFile,typeName,stateNames);
			}
			else
				visitor = new GenericCsvStatsVisitor(csvFile,typeName);

			System.out.println("accepting");
			GfStatsReader reader = new GfStatsReader(archiveFile.getAbsolutePath());
			reader.accept(visitor);

		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}


package gedi.solutions.geode.client;

import java.io.File;

import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;

import nyla.solutions.core.exception.SystemException;
import nyla.solutions.core.io.IO;
import nyla.solutions.core.patterns.jmx.JMX;
import nyla.solutions.core.util.Config;

/**
 * This class contains a set of utility operations to support QA testing
 * @author Gregory Green
 *
 */
public class SingletonGemFireJmx
{
	private static JMX jmx = null;

	/**
	 *
	 * @param directory the directory the clear
	 * @param pattern the folder name match pattern
	 */
	public static void clearDirectory(File directory, String pattern)
	{

		File[] files = IO.listFiles(directory, pattern);

		if(files == null || files.length == 0)
			return;

		for (File file : files)
		{
			if(!file.delete())
				throw new SystemException("cannot delete:"+file);

		}
	}// --------------------------------------------------------
	/**
	 * Close and recreate the JMX connect
	 * @return the JMX connection
	 */
	public synchronized static JMX reconnect()
	{
		try
		{

				ClientCache cache = null;

				cache = ClientCacheFactory.getAnyInstance();

				if(cache != null && !cache.isClosed())
				{
						cache.close();
				}
		}
		catch (Exception e)
		{
				System.out.println("Cache was closed");
		}

		if(jmx != null)
		{

			jmx.dispose();
			jmx = null;
		}

		return getJmx();
	}// --------------------------------------------------------
	/**
	 * Connect/Reconnect to a locator host/port
	 * @param locatorHost the locator host
	 * @param locatorPort the locator port
	 * @return the JMX connection
	 */
	public static synchronized JMX reconnectJMX(String locatorHost, int locatorPort)
	{

		try
		{

				ClientCache cache = null;

				cache = ClientCacheFactory.getAnyInstance();

				if(cache != null && !cache.isClosed())
				{
						cache.close();
				}

		}
		catch (Exception e)
		{
				System.out.println("Cache was closed");
		}

		if(jmx != null)
		{

			jmx.dispose();
			jmx = null;
		}

		SingletonGemFireJmx.setLocatorJmxHost(locatorHost);
		SingletonGemFireJmx.setLocatorPort(locatorPort);

		return getJmx();
	}// --------------------------------------------------------

	public synchronized static JMX getJmx()
	{
		if(jmx == null)
			jmx = JMX.connect(locatorJmxHost,locatorJmxPort);

		return jmx;
	}

	public static void dispose()
	{
		if(jmx != null) try{ jmx.dispose(); } catch(Exception e){e.printStackTrace();}
		jmx = null;
	}

	/**
	 * @return the locatorHost
	 */
	public static String getLocatorHost()
	{
		return locatorJmxHost;
	}

	/**
	 * @param locatorJmxHost the locatorHost to set
	 */
	public static void setLocatorJmxHost(String locatorJmxHost)
	{
		SingletonGemFireJmx.locatorJmxHost = locatorJmxHost;
	}

	/**
	 * @return the locatorJmxPort
	 */
	public static int getLocatorJmxPort()
	{
		return locatorJmxPort;
	}

	/**
	 * @param locatorPort the locatorPort to set
	 */
	public static void setLocatorPort(int locatorPort)
	{
		SingletonGemFireJmx.locatorJmxPort = locatorPort;
	}

	private static String locatorJmxHost = Config.getProperty(SingletonGemFireJmx.class,"locatorJmxHost");
	private static int locatorJmxPort = Config.getPropertyInteger(SingletonGemFireJmx.class,"locatorJmxPort").intValue();
}


package gedi.solutions.geode.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.QueryExp;
import javax.management.ValueExp;
import org.apache.geode.management.DistributedSystemMXBean;
import org.apache.geode.management.MemberMXBean;
import gedi.solutions.geode.client.GemFireJmxClient;
import gedi.solutions.geode.client.SingletonGemFireJmx;
import nyla.solutions.core.patterns.jmx.JMX;
import nyla.solutions.core.util.Debugger;

/**
 * This object handles operations on the cluster
 * such as shutting down members.
 *
 * @author Gregory Green
 *
 */
public class GemFireMgmt
{
	/**
	 * Stops all cache servers followed by locators on a given server
	 * @param hostName the host name and or IP address
	 * @return the number of members
	 */
	public static int stopMembersOnHost(String hostName)
	{

		JMX jmx = SingletonGemFireJmx.getJmx();

		String objectNamePattern = "GemFire:type=Member,member=*";
		QueryExp queryExp = null;
		ValueExp[] values = null;

		//Also get the IP
		try
		{
			InetAddress[] addresses = InetAddress.getAllByName(hostName);
			InetAddress address = null;
			if(addresses != null)
			{
				values = new ValueExp[addresses.length];

				for (int i=0; i <addresses.length;i++)
				{
					address = addresses[i];
					values[i] = Query.value(address.getHostAddress());

				}
			}
		}
		catch (UnknownHostException e)
		{
			Debugger.println(e.getMessage());
		}

		if(values != null)
		{
			queryExp = Query.or(Query.eq(Query.attr("Host"), Query.value(hostName)),
								Query.in(Query.attr("Host"), values));
		}
		else
		{
			queryExp = Query.eq(Query.attr("Host"), Query.value(hostName));
		}

		/*
		 * QueryExp query = Query.and(Query.eq(Query.attr("Enabled"), Query.value(true)),
               Query.eq(Query.attr("Owner"), Query.value("Duke")));
		 */
		Set<ObjectName> memberObjectNames =  jmx.searchObjectNames(objectNamePattern, queryExp);

		if(memberObjectNames == null || memberObjectNames.isEmpty())
			return 0;

		int memberCount = memberObjectNames.size();

		MemberMXBean member = null;

		Collection<String> locators = new ArrayList<String>();
		for (ObjectName objectName : memberObjectNames)
		{
			member = GemFireJmxClient.getMember(objectName.getKeyProperty("member"), SingletonGemFireJmx.getJmx());

			if(member.isLocator())
			{
				locators.add(member.getName());
			}
			else
			{
				shutDownMember(member.getName());
			}

		}

		for (String locatorName : locators)
		{
			shutDownMember(locatorName);
		}

		return memberCount;
	}// --------------------------------------------------------
	/**
	 * Shut down a given member by its name
	 * @param name cache server name
	 */
	public static void shutDownMember(String name)
	{
		try
		{
			ObjectName serverName = new ObjectName("GemFire:type=Member,member="+name);

			JMX jmx = SingletonGemFireJmx.getJmx();

			MemberMXBean bean = jmx.newBean(MemberMXBean.class,serverName);

			bean.shutDownMember();

			//wait for member to shutdown
			System.out.println("Waiting for member:"+name+"  to shutdown");
			while(GemFireJmxClient.checkMemberStatus(name,SingletonGemFireJmx.getJmx()))
			{

				Thread.sleep(shutDownDelay);
			}
		}
		catch (MalformedObjectNameException e)
		{
			throw new RuntimeException("Unable to shutdown member "+name
					+" ERROR:"+e.getMessage(),e);
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
	}// --------------------------------------------------------

	public static void stopLocator(JMX jmx,String locatorName)
	{
		try
		{
			ObjectName objectName  = new ObjectName("GemFire:type=Member,member="+locatorName);

			//DistributedSystemMXBean distributedSystemMXBean =
			MemberMXBean bean = jmx.newBean(MemberMXBean.class, objectName);

			bean.shutDownMember();
		}
		catch (MalformedObjectNameException e)
		{
			throw new RuntimeException("Cannot stop member:"+locatorName+" ERROR:"+e.getMessage(),e);
		}

	}//------------------------------------------------

	//Does not stop locators
	public static String [] shutDown(JMX jmx)
	{
		try
		{

			DistributedSystemMXBean bean = toDistributeSystem(jmx);

			return bean.shutDownAllMembers();

		}
		catch (Exception e)
		{
			throw new RuntimeException(" ERROR:"+e.getMessage(),e);
		}

	}// --------------------------------------------------------
	private static DistributedSystemMXBean toDistributeSystem(JMX jmx) throws MalformedObjectNameException
	{
		ObjectName objectName  = new ObjectName("GemFire:service=System,type=Distributed");

		//DistributedSystemMXBean distributedSystemMXBean =
		DistributedSystemMXBean bean = jmx.newBean(DistributedSystemMXBean.class, objectName);
		return bean;
	}//------------------------------------------------
	/**
	 * Dispose of the JMX/GemFire connection
	 */
	public static void disconnect()
	{
		SingletonGemFireJmx.dispose();
	}// --------------------------------------------------------
	/**
	 * Reconnect to a given locator
	 * @param locatorHost the locator host
	 * @param locatorPort the locator port
	 * @return the new JMX connect
	 */
   public static JMX reconnectJMX(String locatorHost, int locatorPort)
   {
	   return SingletonGemFireJmx.reconnectJMX(locatorHost, locatorPort);
   }// --------------------------------------------------------
   /**
    * Shut down each member in a given RedundancyZone
    * @param redundancyZone the Redundancy Zone to shutdown
    */
   public static void shutDownRedundancyZone(String redundancyZone)
   {
	   if (redundancyZone == null || redundancyZone.length() == 0)
		   throw new IllegalArgumentException("redundancyZone required");

	   String objectNamePattern = "GemFire:type=Member,member=*";
	   QueryExp exp = Query.eq(Query.attr("RedundancyZone"),Query.value(redundancyZone));

	   Collection<ObjectName> memberObjectNames = SingletonGemFireJmx.getJmx().searchObjectNames(objectNamePattern, exp);
	   for (ObjectName objectName : memberObjectNames)
	   {
		   GemFireMgmt.shutDownMember(objectName.getKeyProperty("member"));
	   }
   }// --------------------------------------------------------
   private static long shutDownDelay = 1000;
}


package gedi.solutions.geode.lucene;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import org.apache.geode.cache.Region;

import gedi.solutions.geode.io.Querier;
import nyla.solutions.core.util.BeanComparator;
import nyla.solutions.core.util.Organizer;

public class GeodePagination
{
	/**
	 *
	 * @param id the unique ID
	 * @param pageNumber the page number
	 * @param pageRegion the page region
	 * @return the collection
	 */
	public Collection<?> readKeys(String id, int pageNumber ,Region<String,Collection<?>> pageRegion)
	{
		String pageKey = toPageKey(id, pageNumber);

		return pageRegion.get(pageKey);
	}// --------------------------------------------------------------

	/**
	 * Store the pagination search result details
	 * @param <K> the key class
	 * @param <V> the value class
	 * @param id the unique id
	 * @param pageSize the page size
	 * @param pageKeysRegion the region where pages keys are stors
	 * @param results the collection os results
	 * @return the list of keys in the page region
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <K, V> List<String> storePaginationMap(String id,int pageSize,
			Region<String, Collection<K>> pageKeysRegion,
			List<Map.Entry<K, V>> results)
	{

		if(results == null || results.isEmpty())
			return null;

		//add to pages
		 List<Collection<K>> pagesCollection = toKeyPages((List)results, pageSize);

		 int pageIndex = 1;
		 String key = null;
		 ArrayList<String> keys = new ArrayList<String>(pageSize);
		 for (Collection<K> page : pagesCollection)
		{
			 //store in region
			 key = toPageKey(id,pageIndex++);

			 pageKeysRegion.put(key, page);

			 keys.add(key);
		}

		 keys.trimToSize();
		return keys;
	}// --------------------------------------------------------------
	/***
	 *
	 * @param id the ID
	 * @param pageNumber the page number
	 * @return the id-pageNumber
	 */
	public static String toPageKey(String id,int pageNumber)
	{
		return new StringBuilder().append(id).append("-").append(pageNumber).toString();
	}// --------------------------------------------------------------

	/**
	 * @param <K> the key class
	 * @param <V> the value class
	 * @param mapEntries the map entries
	 * @param pageSize the page size
	 * @return the collection of key
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <K,V> List<Collection<K>> toKeyPages(List<Map.Entry<K, V>> mapEntries,int pageSize)
	{
		if(mapEntries == null || mapEntries.isEmpty())
			return null;

		int collectionSize = mapEntries.size();

		if(pageSize <= 0 || collectionSize <= pageSize)
		{
			ArrayList<K> list = new ArrayList<K>(mapEntries.size());
			for (Map.Entry<K, V> entry : mapEntries)
			{
				if(entry == null)
					continue;

				list.add(entry.getKey());
			}

			if(list.isEmpty())
				return null;

			return Collections.singletonList(list);
		}

		int initialSize = collectionSize /pageSize;

		ArrayList<Collection<K>> list = new ArrayList(initialSize);

		ArrayList<K> current = new ArrayList<K>();
		for (Map.Entry<K, V> entry : mapEntries)
		{
			current.add(entry.getKey());

			if(current.size() >= pageSize)
			{
				current.trimToSize();

				list.add((Collection<K>)current);
				current = new ArrayList<K>();
			}
		}

		if(!current.isEmpty())
			list.add((Collection<K>)current);

		return (List<Collection<K>>)list;

	}//------------------------------------------------
	public List<String> storePagination(String sanId, int pageSize,
			Map<String, Collection<?>> pageKeysRegion,
			Collection<String> keys)
	{
		if(keys == null || keys.isEmpty())
			return null;

		//add to pages
		 List<Collection<String>> pagesCollection = Organizer.toPages(keys, pageSize);

		 String key = null;
		 int pageIndex = 1;
		 ArrayList<String> pageKeys = new ArrayList<String>(10);
		 for (Collection<String> page : pagesCollection)
		{
			 //store in region
			 key = new StringBuilder().append(sanId).append("-").append(pageIndex++).toString();

			 pageKeysRegion.put(key, page);

			 pageKeys.add(key);
		}

		 pageKeys.trimToSize();
		return pageKeys;
	}
	/**
	 * Read Results from region by keys in pageRegion
	 * @param <K> the key class
	 * @param <V> the value class
	 * @param criteria the text page criteria
	 * @param pageNumber the page number to retrieve
	 * @param region the region where data is retrieved
	 * @param pageRegion the page region with key contains the pageKey and value the keys to the region
	 * @return region.getAll(pageKey)
	 */
	public <K,V> Map<K,V> readResultsByPage(TextPageCriteria criteria, int pageNumber, Region<K,V> region, Region<String,Collection<?>> pageRegion)
	{
		if(pageRegion == null )
			return null;

		Collection<?> regionKeys = pageRegion.get(criteria.toPageKey(pageNumber));

		if(regionKeys == null|| regionKeys.isEmpty())
			return null;

		return region.getAll(regionKeys);
	}

	@SuppressWarnings("unchecked")
	public <K,V> Collection<V> readResultsByPageValues(String criteriaId, String sortField, boolean desc,int pageNumber, Region<K,V> region, Region<String,Collection<?>> pageRegion)
	{
		if(pageRegion == null )
			return null;

		Collection<K> regionKeys = (Collection<K>)pageRegion.get(criteriaId+"-"+pageNumber);

		if(regionKeys == null|| regionKeys.isEmpty())
			return null;

		if(sortField != null)
		{
			String field = sortField.replace("entry.", "");
			BeanComparator c = new BeanComparator(field,desc);

			TreeSet<V> set = new TreeSet<V>(c);

			for (K key : regionKeys)
			{
				set.add(region.get(key));
			}

			return set;
		}
		else
		{

			ArrayList<V> list = new ArrayList<V>();
			for (K key : regionKeys)
			{
				list.add(region.get(key));
			}

			return list;

		}
	}//------------------------------------------------
	public <K> Collection<String> clearSearchResultsByPage(TextPageCriteria criteria, Region<String,Collection<K>> pageRegion)
	{
		///TODO: Get previous page numbers faster (without a query)
		Collection<String> pageKeys = Querier.query("select * from /"+criteria.getPageRegionName()+".keySet() k where k like '"+criteria.getId()+"%'");

		if(pageKeys == null || pageKeys.isEmpty())
			return null;

		pageRegion.removeAll(pageKeys);

		return pageKeys;
	}//------------------------------------------------
}


package gedi.solutions.geode.operations.functions;

import java.io.File;
import java.util.Properties;

import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.RegionFunctionContext;
import org.apache.geode.cache.execute.ResultSender;
import org.apache.geode.cache.snapshot.SnapshotOptions.SnapshotFormat;
import org.apache.logging.log4j.LogManager;

import gedi.solutions.geode.data.ExportFileType;
import nyla.solutions.core.util.Debugger;

/**
 * <pre>
 * Import region data using exported GFD formatted data.
 * </pre>
 * @author Gregory Green
 *
 */
public class GfdImportFunction implements Function<Object>, Declarable
{

	public GfdImportFunction()
	{
	}// ------------------------------------------------

	public void execute(FunctionContext<Object> fc)
	{
		ResultSender<Object> rs = fc.getResultSender();

		try
		{
			boolean results = false;
			if (fc instanceof RegionFunctionContext)
			{
				results = importOnRegion((RegionFunctionContext) fc);
			} else
			{
				String[] args = (String[])fc.getArguments();

				if(args == null || args.length == 0)
					throw new IllegalArgumentException("Arguments with region name required");

				String regionName = args[0];
				this.importRegion(regionName);
			}

			rs.lastResult(results);
		}
		catch (Exception e)
		{
			String stackTrace = Debugger.stackTrace(e);

			LogManager.getLogger(getClass()).error(stackTrace);
			rs.sendException(e);

			throw new FunctionException(stackTrace);
		}
	}// --------------------------------------------------------

	private boolean importRegion(String regionName) throws Exception
	{
		if(regionName == null || regionName.length() == 0)
			return false;

		Cache cache = CacheFactory.getAnyInstance();

		Region<Object, Object> region = cache.getRegion(regionName);

		return importRegion(region);

	}// ------------------------------------------------

	protected boolean importOnRegion(RegionFunctionContext rfc) throws Exception
	{
		// get argument

		// check if region is partitioned

		Region<Object, Object> region = rfc.getDataSet();

		return importRegion(region);
	}// ------------------------------------------------

	/**
	 * Import exported data from a given
	 * @param region the region to be import
	 * @return true is the import was successful
	 * @throws Exception
	 */
	private boolean importRegion(Region<Object, Object> region)
			throws Exception
	{
		File file = DataOpsSecretary.determineFile(ExportFileType.gfd, region.getName());

		if(!file.exists())
			return false;

		region.getSnapshotService().load(file, SnapshotFormat.GEMFIRE);

		return true;
	}// ------------------------------------------------

	/***
	 * @return ImportJsonFunction
	 */
	public String getId()
	{

		return "GfdImportFunction";
	}

	public boolean hasResult()
	{
		return true;
	}

	public boolean isHA()
	{
		return true;
	}

	public boolean optimizeForWrite()
	{
		return true;
	}// --------------------------------------------------------
	@Override
	public void init(Properties properties)
	{
	}// --------------------------------------------------------

	/**
	 *
	 */
	private static final long serialVersionUID = -3148806554381339703L;

}


package gedi.solutions.geode.util;

import nyla.solutions.core.net.Networking;

/**
 * Utility class for various common GF related functionality
 *
 * @author Gregory Green
 *
 */
public final class GemFireNetworking
{
	/**
	 * Parse the remote locators and locator and assert that they match
	 * @param remoteLocators the remote locator
	 * @param locators the  local locator
	 * @return true if locators match
	 */
	public static boolean checkRemoteLocatorsAndLocatorsMatch(String remoteLocators, String locators)
	{

		if(remoteLocators == null || remoteLocators.length() == 0)
			return false;

		if(remoteLocators.equalsIgnoreCase(locators))
			return true;

		String[] remoteLocatorsArray = remoteLocators.split(",");

		if(locators == null || locators.length() == 0)
			return false;

		String[] locatorsArray = locators.split(",");

		String remoteLocatorHost, locatorHost;
		int remoteLocatorPort, locatorPort;

			for (String remoteLocator : remoteLocatorsArray)
			{
				if(remoteLocator == null || remoteLocator.length() == 0)
					continue;

				//parse host
				for(String locator: locatorsArray)
				{
					if(locator == null || locator.length() == 0)
						continue;

					try
					{
						remoteLocatorHost = parseLocatorHost(remoteLocator);
						locatorHost = parseLocatorHost(locator);
						remoteLocatorPort = parseLocatorPort(remoteLocator);
						locatorPort = parseLocatorPort(locator);

						if(Networking.hostEquals(remoteLocatorHost,locatorHost)
							&& remoteLocatorPort == locatorPort)
						{
							return true;
						}
						else
						{
							//check if ip address match

						}
					}
					catch (NumberFormatException e)
					{
						//port parse exception
						return false;
					}
					catch (IllegalArgumentException e)
					{
						throw new IllegalArgumentException("remoteLocator:"+remoteLocator+" locator:"+locator+" ERROR:"+e.getMessage(),e);
					}
				}
			}

			return false;

	}// --------------------------------------------------------
	private static String parseLocatorHost(String locator)
	{

		int i = locator.indexOf("[");

		if(i > 0)
			return locator.substring(0,i).trim();
		else
			return locator.trim();
	}// --------------------------------------------------------
	private static int parseLocatorPort(String locator)
	{
		int start = locator.indexOf("[");

		String text = null;
		if(start > 0)
		{
			String results = locator.substring(start+1);

			int end = results.indexOf("]");

			if(end > 0)
			{
				text = results.substring(0,end).trim();
				return Integer.parseInt(text);
			}
		}

		throw new IllegalArgumentException("Expected format host[port] but provided with:"+locator);

	}// --------------------------------------------------------

}


package gedi.solutions.geode.operations.gfd;

import java.io.File;
import java.io.IOException;

import org.apache.geode.internal.cache.snapshot.SnapshotPacket.SnapshotRecord;

import nyla.solutions.core.util.Config;

/**
 *
 * Splits a export file into batches
 *
 * @author Gregory Green
 *
 */
@Deprecated
public class SplitGfdFile implements Runnable
{
	/**
	 * Default constructor
	 */
	public SplitGfdFile()
	{
	}// ------------------------------------------------

	@Override
	public void run()
	{
		if (!outDirectory.exists())
		{
			throw new IllegalArgumentException(outDirectory + " does not exist");
		}

		// read

		try(SnapshotRecordReader reader = new SnapshotRecordReader(inFile))
		{

			SnapshotRecord record = null;

			// create writer
			int batchCount = 1;
			File out = createFile(batchCount++);
			SnapshotRecordWriter writer = null;

			try
			{
				writer = new SnapshotRecordWriter(out, regionName);
				int cnt = 0;
				while ((record = reader.readSnapshotRecord()) != null)
				{
					writer.writeSnapshotEntry(record);
					cnt++;

					if (cnt >= batchSize)
					{
						out = createFile(batchCount++);
						writer.close();

						writer = new SnapshotRecordWriter(out, regionName);
					}
				}
			}
			finally
			{
				if(writer != null)
					writer.close();
			}
		}
		catch (ClassNotFoundException | IOException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
	}// ------------------------------------------------

	File createFile(int batchCount)
	{
		return new File(outDirectory.getPath(), regionFileName + batchCount + ".gfd");

	}

	public static void main(String[] args) throws Exception
	{
		if(args == null || args.length == 0)
		{
			System.err.println("Usage: java "+SplitGfdFile.class.getName()+" region file outDir batchSize");
			System.exit(-1);
		}
		SplitGfdFile split = new SplitGfdFile();

		String regionName = args[0];
		File file = new File(args[1]);
		File directory = new File(args[2]);

		split.setRegionName(regionName);
		split.setInFile(file);
		split.setOutDirectory(directory);

		int batchSize = Integer.valueOf(args[3]).intValue();
		split.setBatchSize(batchSize);

		split.run();

	}

	/**
	 * @return the outDirectory
	 */
	public File getOutDirectory()
	{
		return outDirectory;
	}

	/**
	 * @return the inFile
	 */
	public File getInFile()
	{
		return inFile;
	}

	/**
	 * @return the regionName
	 */
	public String getRegionName()
	{
		return regionName;
	}

	/**
	 * @param outDirectory
	 *            the outDirectory to set
	 */
	public void setOutDirectory(File outDirectory)
	{
		this.outDirectory = outDirectory;
	}

	/**
	 * @param inFile
	 *            the inFile to set
	 */
	public void setInFile(File inFile)
	{
		this.inFile = inFile;
	}

	/**
	 * @param regionName
	 *            the regionName to set
	 */
	public void setRegionName(String regionName)
	{

		this.regionName = regionName;

		regionFileName  = this.regionName;

		if (this.regionFileName.startsWith("/"))
		{
			this.regionFileName = regionName.substring(1);
		}

		regionFileName = regionFileName.replace('/', '-');
	}// ------------------------------------------------

	/**
	 * @return the batchSize
	 */
	public int getBatchSize()
	{
		return batchSize;
	}

	/**
	 * @param batchSize
	 *            the batchSize to set
	 */
	public void setBatchSize(int batchSize)
	{
		this.batchSize = batchSize;
	}

	private File outDirectory;
	private File inFile;
	private String regionName;
	private String regionFileName;
	private int batchSize = Config.getPropertyInteger(SplitGfdFile.class, "bathSize", 100);
}


package gedi.solutions.geode.operations.functions;

import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.ResultSender;
import org.apache.logging.log4j.LogManager;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class PrintExampleJsonFunction implements Function<Object>
{
	public void execute(FunctionContext<Object> fc)
	{
		ResultSender<String> rs = fc.getResultSender();

		String[] args = (String[]) fc.getArguments();
		try
		{

			if(args == null || args.length == 0)
				throw new FunctionException("args");

			String className = args[0];

			if(className == null || className.length() == 0)
			{
				rs.lastResult("{}");
				return;
			}

			ObjectMapper objectMapper = new ObjectMapper();

			Object obj = forClassName(className).newInstance();

			rs.lastResult(objectMapper.writeValueAsString(obj));
		}

		catch (ClassNotFoundException| IllegalAccessException | InstantiationException | JsonProcessingException| RuntimeException e)
		{
			LogManager.getLogger(getClass()).error(e.toString());
			rs.sendException(e);
		}
	}// --------------------------------------------------------
	private Class<?> forClassName(String className)
	throws ClassNotFoundException
	{
		if(className == null || className.length() == 0)
			throw new FunctionException("class name string is empty: ");

		return Class.forName(className);
	}// --------------------------------------------------------

	public String getId()
	{
		return "PrintExampleJsonFunction";
	}

	public boolean hasResult()
	{
		return true;
	}

	public boolean isHA()
	{
		return false;
	}

	public boolean optimizeForWrite()
	{
		return true;
	}

	/**
	 *
	 */
	private static final long serialVersionUID = 8579019703991148580L;

}


package gedi.solutions.geode.operations.stats;

import java.io.PrintWriter;

import gedi.solutions.geode.operations.stats.visitors.StatsVisitor;

public class BitSeries implements StatsInfo
{
	   int count; // number of items in this series
	    long currentStartBits;
	    long currentEndBits;
	    long currentInterval;
	    int currentCount;
	    int intervalIdx; // index of most recent BitInterval
	    BitInterval intervals[];

	    /*
	     * Returns the amount of memory used to implement this series.
	     */
	    protected int getMemoryUsed() {
	      int result = 4 + 8 + 8 + 8 + 4 + 4 + 4;
	      if (intervals != null) {
	        result += 4 * intervals.length;
	        for (int i = 0; i <= intervalIdx; i++) {
	          result += intervals[i].getMemoryUsed();
	        }
	      }
	      return result;
	    }

	    /*
	     * Gets the first "resultSize" values of this series skipping over the first
	     * "samplesToSkip" ones. The first value in a series is at index 0. The
	     * maximum result size can be obtained by calling "getSize()".
	     */
	    public double[] getValuesEx(int typeCode, int samplesToSkip,
	        int resultSize) {
	      double[] result = new double[resultSize];
	      int firstInterval = 0;
	      int idx = 0;
	      while (samplesToSkip > 0
	          && firstInterval <= intervalIdx
	          && intervals[firstInterval].getSampleCount() <= samplesToSkip) {
	        samplesToSkip -= intervals[firstInterval].getSampleCount();
	        firstInterval++;
	      }
	      for (int i = firstInterval; i <= intervalIdx; i++) {
	        idx += intervals[i].fill(result, idx, typeCode, samplesToSkip);
	        samplesToSkip = 0;
	      }
	      if (currentCount != 0) {
	        idx += BitInterval.create(currentStartBits, currentInterval,
	            currentCount).fill(result, idx, typeCode, samplesToSkip);
	      }
	      // assert
	      if (idx != resultSize) {
	        throw new RuntimeException("GetValuesEx didn't fill the last "
	            + (resultSize - idx) + " entries of its result");
	      }
	      return result;
	    }

	    void dump(PrintWriter stream) {
	      stream.print("[size=" + count + " intervals=" + (intervalIdx + 1)
	          + " memused=" + getMemoryUsed() + " ");
	      for (int i = 0; i <= intervalIdx; i++) {
	        if (i != 0) {
	          stream.print(", ");
	        }
	        intervals[i].dump(stream);
	      }
	      if (currentCount != 0) {
	        if (intervalIdx != -1) {
	          stream.print(", ");
	        }
	        BitInterval.create(currentStartBits, currentInterval,
	            currentCount).dump(stream);
	      }
	      stream.println("]");
	    }

	    BitSeries() {
	      count = 0;
	      currentStartBits = 0;
	      currentEndBits = 0;
	      currentInterval = 0;
	      currentCount = 0;
	      intervalIdx = -1;
	      intervals = null;
	    }

	    void initialBits(long bits) {
	      this.currentEndBits = bits;
	    }

	    int getSize() {
	      return this.count;
	    }

	    void addBits(long deltaBits) {
	      long bits = currentEndBits + deltaBits;
	      if (currentCount == 0) {
	        currentStartBits = bits;
	        currentCount = 1;
	      } else if (currentCount == 1) {
	        currentInterval = deltaBits;
	        currentCount++;
	      } else if (deltaBits == currentInterval) {
	        currentCount++;
	      } else {
	        // we need to move currentBits into a BitInterval
	        if (intervalIdx == -1) {
	          intervals = new BitInterval[2];
	          intervalIdx = 0;
	          intervals[0] = BitInterval.create(currentStartBits, currentInterval,
	              currentCount);
	        } else {
	          if (!intervals[intervalIdx].attemptAdd(currentStartBits,
	              currentInterval, currentCount)) {
	            // wouldn't fit in current bit interval so add a new one
	            intervalIdx++;
	            if (intervalIdx >= intervals.length) {
	              BitInterval[] tmp = new BitInterval[intervals.length * 2];
	              System.arraycopy(intervals, 0, tmp, 0, intervals.length);
	              intervals = tmp;
	            }
	            intervals[intervalIdx] = BitInterval.create(currentStartBits,
	                currentInterval, currentCount);
	          }
	        }
	        // now start a new currentBits
	        currentStartBits = bits;
	        currentCount = 1;
	      }
	      currentEndBits = bits;
	      count++;
	    }

	    /*
	     * Free up any unused memory
	     */
	    void shrink() {
	      if (intervals != null) {
	        int currentSize = intervalIdx + 1;
	        if (currentSize < intervals.length) {
	          BitInterval[] tmp = new BitInterval[currentSize];
	          System.arraycopy(intervals, 0, tmp, 0, currentSize);
	          intervals = tmp;
	        }
	      }
	    }

	@Override
	public void accept(StatsVisitor visitor)
	{
		// TODO Auto-generated method stub

	}

}


package gedi.solutions.geode.operations.functions;

import java.io.File;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.RegionFunctionContext;
import org.apache.geode.cache.execute.ResultSender;
import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.geode.cache.snapshot.RegionSnapshotService;
import org.apache.geode.cache.snapshot.SnapshotOptions.SnapshotFormat;
import org.apache.geode.internal.cache.snapshot.SnapshotOptionsImpl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import gedi.solutions.geode.data.ExportFileType;
import nyla.solutions.core.util.Debugger;

/**
 * <pre>
 *
 * Exports the GDF binary containing all region data on the server.
 *
 * The output will be stored in the current working directory.
 * One file will be written per region (format: ${region.name}.gfd)
 *
 * Example:			gfsh>gexecute function --id="GfdExportFunction" --region-myRegion
 * </pre>
 *
 * @author Gregory Green
 *
 */
public class GfdExportFunction  implements Function<Object>, Declarable
{
	/**
	 * String keyFileExtension = ".key"
	 */
	public static final String keyFileExtension = ".key";

	public GfdExportFunction()
	{
	}// ------------------------------------------------

	/**
	 * Export region data in JSON format
	 * @param fc the function context
	 */
	public void execute(FunctionContext<Object> fc)
	{

		ResultSender<Object> rs = fc.getResultSender();
		try
		{
			boolean didExport = false;

			if(fc instanceof RegionFunctionContext)
			{
				didExport = exportOnRegion((RegionFunctionContext)fc);
			}
			else
			{

				//get region name from argument
				String[] args = (String[])fc.getArguments();

				if(args == null || args.length == 0)
					throw new IllegalArgumentException("Region name argument required");

				String regionName = args[0];

				Cache cache = CacheFactory.getAnyInstance();

				Region<Object,Object> region = cache.getRegion(regionName);

				if(region != null)
					didExport = exportRegion(region);
				else
					didExport = false;

			}

			rs.lastResult(didExport);
		}
		catch (Exception e)
		{

			String stackTrace = Debugger.stackTrace(e);

			FunctionException functionException = new FunctionException(stackTrace);

			LogManager.getLogger(getClass()).error(stackTrace);
			rs.sendException(functionException);
			throw functionException;
		}

	}// --------------------------------------------------------

	private boolean exportOnRegion(RegionFunctionContext rfc)
	{
		//get argument

		//check if region is partitioned

		Region<Object,Object> region = rfc.getDataSet();

	    return exportRegion(region);
	}// ------------------------------------------------

	protected boolean  exportRegion(Region<Object, Object> region)
	{
		if(region  == null)
			return false;

		if(PartitionRegionHelper.isPartitionedRegion(region))
		{
			region = PartitionRegionHelper.getLocalData(region);
		}

		Logger logger = LogManager.getLogger(getClass());

		logger.info("Exporting region"+region.getName());

		//get name
	    String regionName = region.getName();

		File resultFile = DataOpsSecretary.determineFile(ExportFileType.gfd, regionName);

		//delete previous
		logger.info("deleting file:"+resultFile.getAbsolutePath());
		boolean wasDeleted = resultFile.delete();

		logger.info("delete:"+wasDeleted);

	    try
		{
			//write data
	    	RegionSnapshotService<?,?> regionSnapshotService = region.getSnapshotService();
	    	SnapshotOptionsImpl<?,?> options = (SnapshotOptionsImpl<?,?>) regionSnapshotService.createOptions();

	    	//setting parallelMode=true will cause only the local region data to export
	    	options.setParallelMode(true);
	    	regionSnapshotService.save(resultFile, SnapshotFormat.GEMFIRE);

			return true;

		}
		catch (RuntimeException e)
		{
			throw e;
		}
	    catch(Exception e)
	    {
	    	throw new FunctionException("Error exporting ERROR:"+ e.getMessage()+" "+Debugger.stackTrace(e));
	    }
	}// ------------------------------------------------

	public String getId()
	{

		return "GfdExportFunction";
	}

	public boolean hasResult()
	{
		return true;
	}

	public boolean isHA()
	{
		return false;
	}

	public boolean optimizeForWrite()
	{
		return false;
	}

	@Override
	public void init(Properties properties)
	{

	}

	/**
	 *
	 */
	private static final long serialVersionUID = -3148806554381339703L;

}


package gedi.solutions.geode.operations.csv.function;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Set;

import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.RegionFunctionContext;
import org.apache.geode.cache.execute.ResultSender;
import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.logging.log4j.LogManager;

import gedi.solutions.geode.operations.csv.CsvHeaderConverter;
import gedi.solutions.geode.operations.csv.CsvRowConverter;
import nyla.solutions.core.io.converter.ConversionFileAuthor;
import nyla.solutions.core.util.Config;
import nyla.solutions.core.util.Organizer;

/**
 * <pre>
 *
 * Exports the CSV string of all region data on the server.
 *
 * The output will be stored in the $gbc-cacheserver-{PERSISTENT_STORE_LOCATION} indicated directory.
 * One file will be written per region (format: ${region.name}.json)
 *
 * Example:			gfsh>execute function --id="ExportJsonFunction" --arguments=myRegion
 * </pre>
 *
 * @author Gregory Green
 *
 */
public class ExportCsvFunction  implements Function<String[]>
{
	/**
	 * EXPORT_DIR_PATH_PROP_NM = "EXPORT_DIR"
	 */
	public static final String EXPORT_DIR_PATH_PROP_NM = "EXPORT_DIR";

	/**
	 *
	 */
	private static final long serialVersionUID = 5996297280142495515L;

	private String directoryPath = Config.getProperty(EXPORT_DIR_PATH_PROP_NM,".");

	private static String fileSeparator = System.getProperty("file.separator");
	private static String suffix = ".csv";

	public ExportCsvFunction()
	{
	}// ------------------------------------------------

	/**
	 * Export region data in JSON format
	 * @param fc the function context
	 */
	public void execute(FunctionContext<String[]> fc)
	{

		ResultSender<Object> rs = fc.getResultSender();
		try
		{
			boolean didExport = false;

			if(fc instanceof RegionFunctionContext)
			{
				String[] args = fc.getArguments();

				didExport = this.exportRegion(((RegionFunctionContext)fc).getDataSet(),
				Organizer.at(0,args));
			}
			else
			{
				didExport = exportRegionByArg(fc);
			}

			rs.lastResult(didExport);
		}
		catch (Exception e)
		{

			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			e.printStackTrace(pw);

			LogManager.getLogger(getClass()).error(sw.toString());
			rs.sendException(e);
		}

	}// --------------------------------------------------------
	private boolean exportRegionByArg(FunctionContext<String[]> fc)
	{

		  String[] args = fc.getArguments();

		    if(args == null || args.length == 0)
		    {
		    	throw new FunctionException("Argument not provided");
		    }

		//Get region name from arguments
		String regionName = Organizer.at(0,args);
		if (regionName == null || regionName.length() == 0)
			throw new FunctionException("regionName is required at argumeng index 0");

		Cache cache = CacheFactory.getAnyInstance();

		Region<Object,Object> region = cache.getRegion(regionName);

		return exportRegion(region,Organizer.at(1, args));

	}// ------------------------------------------------
	/**
	 *
	 * @param region the region
	 * @param args the input String 0=regioName and or
	 * @return
	 */
	boolean  exportRegion(Region<Object, Object> region, String exportFilePath)
	{

		if(PartitionRegionHelper.isPartitionedRegion(region))
		{
			region = PartitionRegionHelper.getLocalData(region);
		}

		//get first

	    Set<Object> keySet = region.keySet();

	    if(keySet == null || keySet.isEmpty())
	    {
	    		return false;
	    }

	    String regionName = region.getName();

	    String memberName = CacheFactory.getAnyInstance().getDistributedSystem().getDistributedMember().getName();

	    if(exportFilePath == null)
	    	exportFilePath = this.directoryPath;

	    File resultFile = new File(new StringBuilder(exportFilePath)
				.append(fileSeparator).append(memberName).append("-").append(regionName).append(suffix).toString());

	    if(resultFile.exists())
	    {
	    		if(!resultFile.delete())
	    			throw new FunctionException("Cannot delete:"+resultFile);
	    }

	    ConversionFileAuthor<Object> author = new ConversionFileAuthor<>(resultFile, new CsvHeaderConverter(), new CsvRowConverter());

	    try
		{

	    	Object value = null;

			for (Object key : keySet)
			{
				value = region.get(key);

				author.appendFile(value);
			}

			return true;

		}
		catch (RuntimeException e)
		{
			throw e;
		}
	    catch(IOException e)
	    {
	    	throw new FunctionException("Error exporting ERROR:"+ e.getMessage(),e);
	    }
	}// ------------------------------------------------

	public String getId()
	{

		return "ExportCsvFunction";
	}

	public boolean hasResult()
	{
		return true;
	}

	public boolean isHA()
	{
		return false;
	}

	public boolean optimizeForWrite()
	{
		return true;
	}

}


package gedi.solutions.geode.lucene;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.geode.cache.lucene.LuceneQuery;
import org.apache.geode.cache.lucene.LuceneQueryException;
import org.apache.geode.cache.lucene.LuceneResultStruct;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.geode.cache.lucene.LuceneServiceProvider;

import gedi.solutions.geode.client.GeodeClient;
import gedi.solutions.geode.io.GemFireIO;
import gedi.solutions.geode.io.Querier;
import gedi.solutions.geode.lucene.function.SimpleLuceneSearchFunction;
import nyla.solutions.core.data.MapEntry;
import nyla.solutions.core.exception.SystemException;
import nyla.solutions.core.util.BeanComparator;
import nyla.solutions.core.util.Organizer;

/**
 * Implement for searching Lucene
 * @author Gregory Green
 *
 */
public class GeodeLuceneSearch
{
	private final LuceneService luceneService;
	/**
	 *
	 * @param gemFireCache the cache
	 */
	public GeodeLuceneSearch(GemFireCache gemFireCache)
	{
		this(LuceneServiceProvider.get(gemFireCache));
	}//------------------------------------------------
	/**
	 *
	 * @param luceneService the luceneService
	 */
	public GeodeLuceneSearch(LuceneService luceneService)
	{
		this.luceneService = luceneService;
	}//------------------------------------------------

	public Collection<String>  saveSearchResultsWithPageKeys(TextPageCriteria criteria, Region<String,Collection<?>> pageKeysRegion)
	{
		if(criteria == null)
			return null;

		if(criteria.getQuery() == null || criteria.getQuery().length() == 0)
			return null;

		if(criteria.getIndexName() == null || criteria.getIndexName().length() == 0)
			throw new IllegalArgumentException("Default criteria's indexName is required");

		if(criteria.getId() == null || criteria.getId().length() == 0)
			throw new IllegalArgumentException("Default criteria's id is required");

		if(criteria.getDefaultField() == null || criteria.getDefaultField().length() == 0)
			throw new IllegalArgumentException("Default criteria's defaultField is required");

		try
		{

			LuceneQuery<Object, Object> luceneQuery = luceneService.createLuceneQueryFactory()
			  .create(criteria.getIndexName(),
			  criteria.getRegionName(),
			  criteria.getQuery(), criteria.getDefaultField());

			 List<LuceneResultStruct<Object, Object>> list = luceneQuery.findResults();

			 luceneQuery.findPages();

			 if(list == null || list.isEmpty())
				 return null;

			 String sortField =  criteria.getSortField();
			 BeanComparator beanComparator = null;

			 Collection<Map.Entry<Object,Object>> results = null;
			 if(sortField != null && sortField.trim().length() > 0 )
			 {
				 beanComparator = new BeanComparator(sortField,criteria.isSortDescending());

				 Collection<Map.Entry<Object,Object>> set = new TreeSet<Map.Entry<Object,Object>>(beanComparator);
				 list.parallelStream().forEach(e -> set.add(new MapEntry<Object,Object>(e.getKey(), e.getValue())));
				 results = set;
			 }
			 else
			 {
				 results = list.stream().map( e -> new MapEntry<>(e.getKey(), e.getValue())).collect(Collectors.toList());
			 }

			 //add to pages
			 List<Collection<Object>> pagesCollection = Organizer.toKeyPages(results, criteria.getEndIndex() - criteria.getBeginIndex());

			 int pageIndex = 0;
			 String key = null;
			 ArrayList<String> keys = new ArrayList<String>(10);
			 for (Collection<Object> page : pagesCollection)
			{
				 //store in region
				 key = new StringBuilder().append(criteria.getId()).append("-").append(pageIndex++).toString();

				 pageKeysRegion.put(key, page);

				 keys.add(key);
			}

			 keys.trimToSize();

			 return keys;

		}
		catch (LuceneQueryException e)
		{
			throw new SystemException(e);
		}
	}//------------------------------------------------
	/**
	 * Read Results from region by keys in pageRegion
	 * @param <K> the key type
	 * @param <V> the value type
	 * @param criteria the text page criteria
	 * @param pageNumber the page number to retrieve
	 * @param region the region where data is retrieved
	 * @param pageRegion the page region with key contains the pageKey and value the keys to the region
	 * @return region.getAll(pageKey)
	 */
	public <K,V> Map<K,V> readResultsByPage(TextPageCriteria criteria, int pageNumber, Region<K,V> region, Region<String,Collection<?>> pageRegion)
	{
		if(pageRegion == null )
			return null;

		Collection<?> regionKeys = pageRegion.get(criteria.toPageKey(pageNumber));

		if(regionKeys == null|| regionKeys.isEmpty())
			return null;

		return region.getAll(regionKeys);
	}

	@SuppressWarnings("unchecked")
	public <K,V> Collection<V> readResultsByPageValues(TextPageCriteria criteria, int pageNumber, Region<K,V> region, Region<String,Collection<?>> pageRegion)
	{
		if(pageRegion == null )
			return null;

		Collection<K> regionKeys = (Collection<K>)pageRegion.get(criteria.toPageKey(pageNumber));

		if(regionKeys == null|| regionKeys.isEmpty())
			return null;

		if(criteria.getSortField() != null)
		{
			String field = criteria.getSortField().replace("entry.", "");
			BeanComparator c = new BeanComparator(field);

			TreeSet<V> set = new TreeSet<V>(c);

			for (K key : regionKeys)
			{
				set.add(region.get(key));
			}

			return set;
		}
		else
		{

			ArrayList<V> list = new ArrayList<V>();
			for (K key : regionKeys)
			{
				list.add(region.get(key));
			}

			return list;

		}
	}//------------------------------------------------
	public Collection<String> clearSearchResultsByPage(TextPageCriteria criteria, Region<String,Collection<?>> pageRegion)
	{
		Collection<String> pageKeys = Querier.query("select * from /"+criteria.getPageRegionName()+".keySet() k where k like '"+criteria.getId()+"%'");

		pageRegion.removeAll(pageKeys);

		return pageKeys;
	}//------------------------------------------------
	@SuppressWarnings("unchecked")
	public <T> Collection<T> search(String indexName,String regionName,String queryString,String defaultField)
	throws Exception
	{
		Region<?,?> region = GeodeClient.connect().getRegion(regionName);

		String[] args = {indexName,regionName,queryString,defaultField};

		return GemFireIO.exeWithResults(FunctionService.onRegion(region).setArguments(args).setArguments(args), new SimpleLuceneSearchFunction());
	}

}


package gedi.solutions.geode.operations.functions;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Iterator;

import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.RegionFunctionContext;
import org.apache.geode.cache.execute.ResultSender;
import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.logging.log4j.LogManager;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;

/**
 *

 * Import region data using exported JSON formatted data.
 *
 * Example:
 * gfsh>execute function --id="ImportJsonFunction" --region=/securityRegion
 *
 *

 *
 * @see ExportJsonFunction
 * @author Gregory Green
 *
 */
public class ImportJsonFunction implements Function<Object>
{
	private String directoryPath = "./";

	private static String fileSeparator = System.getProperty("file.separator");
	private static String suffix = ".json";

	/**
	 * String keyFileExtension = ".key"
	 */
	public static final String keyFileExtension = ".key";

	public ImportJsonFunction()
	{
	}// ------------------------------------------------

	public void execute(FunctionContext<Object> fc)
	{
		ResultSender<Object> rs = fc.getResultSender();

		try
		{
			boolean results = false;
			if (fc instanceof RegionFunctionContext)
			{
				results = importOnRegion((RegionFunctionContext) fc);
			} else
			{
				results = importAllRegions(fc);
			}

			rs.lastResult(results);
		}
		catch (Exception e)
		{
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			e.printStackTrace(pw);

			LogManager.getLogger(getClass()).error(sw.toString());
			rs.sendException(e);

		}

	}// --------------------------------------------------------

	private boolean importAllRegions(FunctionContext<Object> fc) throws Exception
	{

		String[] args = (String[]) fc.getArguments();

		if (args == null || args.length == 0) {
			throw new FunctionException("Argument not provided");
		}

		// Get region name from arguments
		String regionName = args[0];

		Cache cache = CacheFactory.getAnyInstance();

		Region<Object, Object> region = cache.getRegion(regionName);

		return importRegion(region);

	}// ------------------------------------------------

	private boolean importOnRegion(RegionFunctionContext rfc) throws Exception
	{
		// get argument

		// check if region is partitioned

		Region<Object, Object> region = rfc.getDataSet();

		return importRegion(region);
	}// ------------------------------------------------

	private boolean importRegion(Region<Object, Object> region)
			throws Exception
	{
		JsonNode node, keyNode, valueNode, keyClassName, valueClassName;

		Object key, value;

		if (PartitionRegionHelper.isPartitionedRegion(region))
		{
			region = PartitionRegionHelper.getLocalData(region);
		}

		// get first

		ObjectMapper mapper = new ObjectMapper();

		// Configure to be very forgiving
		// mapper.configure(Feature.FAIL_ON_INVALID_SUBTYPE, false);
//		mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//		mapper.configure(Feature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
//		mapper.configure(Feature.FAIL_ON_NUMBERS_FOR_ENUMS, false);
//
//		KeyDeserializers keyDeserializers = new KeyDeserializers()
//		{
//
//			@Override
//			public KeyDeserializer findKeyDeserializer(JavaType javatype,
//					DeserializationConfig deserializationconfig,
//					BeanDescription beandescription, BeanProperty beanproperty)
//					throws JsonMappingException
//			{
//				return new DefaultKeyDeserializer();
//			}
//		};
//
//		mapper.setDeserializerProvider(mapper.getDeserializerProvider()
//				.withAdditionalKeyDeserializers(keyDeserializers));

		// read JSON file
		String filePath = new StringBuilder(this.directoryPath)
				.append(fileSeparator).append(region.getName()).append(suffix)
				.toString();

		File file = new File(filePath);

		if (!file.exists())
		{
			LogManager.getLogger(getClass())
					.info(file.getAbsolutePath() + " does not exists");
			return false;
		}

		try(Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8))
		{

			// TokenBuffer buffer = new TokenBuffer
			JsonNode tree = mapper.readTree(reader);

			Iterator<JsonNode> children = tree.elements();

			if (children == null || !children.hasNext()) {
				return false;
			}

			while (children.hasNext())
			{
				node = children.next();
				keyNode = node.get("key");
				valueNode = node.get("value");
				keyClassName = node.get("keyClassName");
				valueClassName = node.get("valueClassName");

				key = mapper.readValue(keyNode.traverse(),
						forClassName(keyClassName));
				value = mapper.readValue(valueNode.traverse(),
						forClassName(valueClassName));

				region.put(key, value);
			}
			return true;
		}

	}// ------------------------------------------------
	/**
	 *

	 * Inner class that handles Jackson deserialization implementation of the Map key that may
	 * not be strings.
	 *
	 * This methods expects the JSON version of key as the field name.
	 *
	 * "{\"key\":{\"prop1\":\"123\",\"port2\":\"ABC\"},\"keyClassName\":\"exampe.MyObject\"}"
	 *

	 *
	 */
	static class DefaultKeyDeserializer extends KeyDeserializer
	{

		/**
		 * Add the JSON version of the key as the
		 */
		@Override
		public Object deserializeKey(String keyString,
				DeserializationContext deserializationcontext)
				throws IOException, JsonProcessingException
		{

			if (keyString == null || keyString.length() == 0)
			{
				return null;
			}

			ObjectMapper objectMapper = new ObjectMapper();

			// JsonParser jp = deserializationcontext.getParser();

			JsonNode jsonNode = objectMapper.readTree(keyString);

			if (jsonNode.isTextual())
			{
				return keyString;
			}

			// jp.getCodec().readTree(jp);

			String jsonKey = jsonNode.get("key").toString();
			String keyClassName = jsonNode.get("keyClassName").asText();

			try
			{
				return objectMapper.readValue(jsonKey,
						Class.forName(keyClassName));
			}
			catch(UnrecognizedPropertyException e)
			{
				return null;
			}
			catch (ClassNotFoundException e)
			{
				throw new RuntimeException("Cannot create class name:"
						+ keyClassName, e);
			}
		}

	}

	protected Class<?> forClassName(JsonNode jsonNode)
			throws ClassNotFoundException
	{
		if (jsonNode == null) {
			throw new FunctionException("Class Name not found in json string");
		}

		String className = jsonNode.asText();

		if (className == null || className.length() == 0) {
			throw new FunctionException("class name json string is empty: "
					+ jsonNode.toString());
		}

		return Class.forName(className);
	}// --------------------------------------------------------

	/***
	 * @return ImportJsonFunction
	 */
	public String getId()
	{

		return "ImportJsonFunction";
	}

	public boolean hasResult()
	{
		return true;
	}

	public boolean isHA()
	{
		return false;
	}

	public boolean optimizeForWrite()
	{
		return true;
	}

	/**
	 *
	 */
	private static final long serialVersionUID = -3148806554381339703L;

}


package gedi.solutions.geode.qa;

import java.io.IOException;
import java.nio.file.Paths;
import org.apache.geode.cache.RegionShortcut;
import gedi.solutions.geode.util.GemFireInspector;
import gedi.solutions.geode.util.GemFireMgmt;
import nyla.solutions.core.io.IO;
import nyla.solutions.core.operations.Shell;
import nyla.solutions.core.operations.Shell.ProcessInfo;
import nyla.solutions.core.patterns.jmx.JMX;
import nyla.solutions.core.util.Config;
import nyla.solutions.core.util.Debugger;

/**
 *

 *
 * This client support JUNIT testing by providing the ability to start
 * management and stop a simple local GemFire cluster.
 *
 * This particular class starts the member using Gfsh.
 *
 * The Gfsh script location can be configured with configuration property named
 *
 *  gfsh_location
 *
 *  export gfsh_location=....
 *
 *  or JVM property
 *
 *  -Dgfsh_location=...
 *
 *  or add to a file named "config.properties" in the CLASSPATH.
 *

 * @author Gregory Green
 */
public class GUnit
{
	private final static String location = Config.getProperty("gfsh_location");
	private final static String runtimeDir = Config.getProperty("runtime_location","runtime");

	public GUnit()
	{
	}//------------------------------------------------
	/**
	 *

	 * This method will start a single locator and cache server.
	 *
	 * The location will be "locator".
	 * The cache server name will be "server"
	 *
	 *
	 * This particular method starts the member using Gfsh.
	 *
	 * The Gfsh script location can be configured with configuration property named
	 *
	 *  gfsh_location
	 *
	 *  export gfsh_location=....
	 *
	 *  or JVM property
	 *
	 *  -Dgfsh_location=...
	 *
	 *  or add to a file named "config.properties" in the CLASSPATH.
	 *
	 *
	 *  Alternatively you could use org.apache.geode.distributed.LocatorLauncher
	 *  or org.apache.geode.distributed.ServerLauncher to start the process.
	 *
	 *  Since there can only be a single Cache instance, these process should be
	 *  started external of the current JVM.
	 *
	 *

	 * @throws Exception when an unknown exception occurs
	 */
	public void startCluster()
	throws Exception
	{
		IO.mkdir(Paths.get(runtimeDir+"/locator").toFile());
		IO.mkdir(Paths.get(runtimeDir+"/server").toFile());

		Shell shell = new Shell();
		ProcessInfo pi = shell.execute(location+"/gfsh","-e","start locator  --dir=runtime/locator --bind-address=localhost --J=-D=gemfire.jmx-manager-hostname-for-clients=localhost --J=-D=gemfire.jmx-manager-bind-address=localhost --J=-D=gemfire.http-service-bind-address=localhost --http-service-port=0  --name=locator  --port=10334");

		System.out.println(pi.exitValue);
		System.out.println(pi.output);
		System.out.println(pi.error);

		pi = shell.execute(location+"/gfsh",
		"-e","start server --name=server --dir="+runtimeDir+"/server --bind-address=localhost  --server-bind-address=localhost --locators=localhost[10334]");

		System.out.println(pi.exitValue);
		System.out.println("OUTPUT:"+pi.output);
		System.out.println("ERROR:"+pi.error);
	}//------------------------------------------------
	/**
	 *
	 * @param regionName the region name
	 * @param regionShortcut the region type to creates
	 */
	public void createRegion(String regionName,RegionShortcut regionShortcut)
	{

		Shell shell = new Shell();

		 ProcessInfo pi = shell.execute(location+"/gfsh",
		 "-e","connect",
		 "-e","create region --name="+regionName+" --type="+regionShortcut);

		System.out.println(pi.exitValue);
		System.out.println("OUTPUT:"+pi.output);
		System.out.println("ERROR:"+pi.error);

	}//------------------------------------------------
	public void shutdown()
	{
		try(JMX jmx = JMX.connect("localhost", 1099))
		{
			String[] members =  GemFireMgmt.shutDown(jmx);

			Debugger.println("members:"+Debugger.toString(members));

			GemFireMgmt.stopLocator(jmx, "locator");

		}

		try
		{
			IO.delete(Paths.get(runtimeDir+"/server").toFile());
		}
		catch(IOException e) {Debugger.printWarn(e);}

		try
		{
			IO.delete(Paths.get(runtimeDir+"/locator").toFile());
		}
		catch(IOException e) {Debugger.printWarn(e);}

	}//------------------------------------------------

	/**
	 * Wait for a given member to startup
	 * @param jmx the JMX connection
	 * @param member the member to wait for
	 */
	public void waitForMemberStart(String member, JMX jmx)
	{
		boolean isRunning =  false;
		boolean printedStartMember  = false;
		int count = 0;
		while(!isRunning)
		{
			try{ isRunning = GemFireInspector.checkMemberStatus(member,jmx); }
			catch(Exception e) {Debugger.printWarn(e);}

			if(!printedStartMember )
			{
				Debugger.println("Waiting for member:"+member+".  Starting member to continue. "+
			            " You can perform a gfsh status command to confirm whether the member is running");
					printedStartMember = true;
			}

			try{ delay();}catch(Exception e){}

			 if(count > retryCount)
			 {
				 throw new RuntimeException("member:"+member+" did not start after "
			 +retryCount+" checks with a delay of "+sleepDelay+" milliseconds");
			 }

			count++;
		}
	}// --------------------------------------------------------
	/**
	 * Wait for a given member to startup
	 * @param jmx the JMX connection
	 * @param member the member to wait for
	 * @throws InterruptedException when an communication error occurs
	 */
	public void waitForMemberStop(String member, JMX jmx) throws InterruptedException
	{
		boolean isRunning =  true;
		boolean printedStartMember  = false;

		int count  = 0;
		while(isRunning && count < retryCount)
		{
			isRunning = GemFireInspector.checkMemberStatus(member,jmx);

			if(!printedStartMember )
			{
					Debugger.println("Waiting for member:"+member+" to stop.");
					printedStartMember = true;
			}

			delay();

			count++;
		}

		if(isRunning)
		{
			throw new RuntimeException("member:"+member+" failed to stop after "+retryCount+
					" checks with a delay of "+sleepDelay+" milliseconds");
		}
	}// --------------------------------------------------------

	public static void delay()
			throws InterruptedException
	{
		System.out.println("Sleeping for "+sleepDelay+" milliseconds");
		Thread.sleep(sleepDelay);
	}// --------------------------------------------------------

	private final static long   sleepDelay = Config.getPropertyLong(GUnit.class.getName()+".sleepDelay",1000*1); //seconds
	private final static int    retryCount = Config.getPropertyInteger(GUnit.class,"retryCount",45);

}


package gedi.solutions.geode.lucene;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.lucene.LuceneQuery;
import org.apache.geode.cache.lucene.LuceneQueryException;
import org.apache.geode.cache.lucene.LuceneQueryProvider;
import org.apache.geode.cache.lucene.LuceneResultStruct;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.geode.cache.lucene.LuceneServiceProvider;

import nyla.solutions.core.data.MapEntry;
import nyla.solutions.core.util.BeanComparator;
import nyla.solutions.core.util.Debugger;

/**
 * Implement for searching Lucene
 * @author Gregory Green
 *
 */

public class TextPolicySearchStrategy {

    	/**
		 *
		 * @param gemFireCache the cache
		 */
		public TextPolicySearchStrategy(GemFireCache gemFireCache)
		{
			this(LuceneServiceProvider.get(gemFireCache));
		}//------------------------------------------------
		/**
		 *
		 * @param luceneService the luceneService
		 */
		public TextPolicySearchStrategy(LuceneService luceneService)
		{
			this.luceneService = luceneService;
		}//------------------------------------------------

		public <K, V> Collection<String>  saveSearchResultsWithPageKeys(TextPageCriteria criteria,LuceneQueryProvider queryProvider, Predicate<LuceneResultStruct<K, V>> filter, Region<String,Collection<K>> pageKeysRegion)
		{
			return saveSearchResultsWithPageKeys(criteria,queryProvider.toString(),filter, pageKeysRegion);
		}//------------------------------------------------
		public <K, V> Collection<String>  saveSearchResultsWithPageKeys(TextPageCriteria criteria,String query, Predicate<LuceneResultStruct<K, V>> filter, Region<String,Collection<K>> pageKeysRegion)
		{
			if(criteria == null)
				return null;

			if(criteria.getId() == null || criteria.getId().length() == 0)
				throw new IllegalArgumentException("Default criteria's id is required");

			try
			{
				//clearing asynchronously
				GeodePagination pagination = new GeodePagination();

				pagination.clearSearchResultsByPage(criteria, pageKeysRegion);

				 List<Map.Entry<K, V>> results = executeQuery(criteria, query, filter);

				 List<String> keys = pagination.storePaginationMap(criteria.getId(),criteria.getEndIndex() -criteria.getBeginIndex() ,pageKeysRegion,
						 results);

				 return keys;

			}
			catch (LuceneQueryException e)
			{
				throw new FunctionException(e);
			}
		}//------------------------------------------------
		/**
		 *
		 * @param criteria the text page criteria
		 * @param queryProvider the query provider
		 * @param filter filter to the records
		 * @param pageKeysRegion page keys regions
		 * @param pagination the pagination
		 * @return collection of entries
		 * @throws LuceneQueryException
		 */
		<K, V> List<Map.Entry<K, V>> executeQuery(TextPageCriteria criteria,
				LuceneQueryProvider queryProvider,
				Predicate<LuceneResultStruct<K, V>> filter) throws LuceneQueryException
		{

			String query = queryProvider.getQuery(null).toString();

			return executeQuery(criteria, query, filter);
		}
		public <K, V> List<Map.Entry<K, V>> executeQuery(TextPageCriteria criteria, String query,
		Predicate<LuceneResultStruct<K, V>> filter)
		throws LuceneQueryException
		{
			if(query == null || query.length() == 0)
				throw new FunctionException("Query provider results text is empty");

			LuceneQuery<K, V> luceneQuery = null;

			luceneQuery = luceneService.createLuceneQueryFactory()
			.create(criteria.getIndexName(), criteria.getRegionName(),query,criteria.getDefaultField());

			Debugger.println("criteria:"+criteria);

			 List<LuceneResultStruct<K, V>> list = luceneQuery.findResults();

			 if(list == null || list.isEmpty())
			 {
				 Debugger.println(new StringBuilder().append(criteria.getId()).append(" lucene results cnt:0"));
				 return null;
			 }

			 Debugger.println(new StringBuilder().append(criteria.getId()).append(" lucene results cnt:").append(list.size()));

			 if(filter != null)
			 {
				 //filter records
				 list = list.parallelStream().filter(filter).collect(Collectors.toList());
			 }

			 if(list == null || list.isEmpty())
				 return null;

			 Debugger.println(new StringBuilder().append(criteria.getId()).append(" FILTERED lucene results cnt:").append(list.size()));

			 String sortField =  criteria.getSortField();
			 BeanComparator beanComparator = null;

			 List<Map.Entry<K, V>> results = null;
			 if(sortField != null && sortField.length() > 0 )
			 {

				 if(!sortField.startsWith("value."))
					 sortField = "value.".concat(sortField);

				 //sorted results
				 beanComparator = new BeanComparator(sortField,criteria.isSortDescending());

				 Collection<Map.Entry<K,V>> set = new TreeSet<Map.Entry<K,V>>(beanComparator);

				 list.parallelStream().forEach(e -> set.add(new MapEntry<K,V>(e.getKey(), e.getValue())));

				 //convert to array
				 results = new ArrayList<Map.Entry<K, V>>(set);

			 }
			 else
			 {
				 //Unorder results
				 results = list.stream().map( e -> new MapEntry<K,V>(e.getKey(), e.getValue())).collect(Collectors.toList());
			 }

			 return results;
		}

		private final LuceneService luceneService;

}


package gedi.solutions.geode.wan;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.geode.pdx.PdxInstance;
import org.apache.logging.log4j.LogManager;

import nyla.solutions.core.security.MD;
import nyla.solutions.core.util.Debugger;

/**
 * Get Entries Checksum for a given region
 * @author Gregory Green
 *
 */
public class GetEntriesChecksumFunction implements Function<Object>, Declarable
{

	/**
	 *
	 */
	private static final long serialVersionUID = 3967250792473591101L;

	/**
	 * This function gets HashMap key=Serializable value=BigInteger
	 *
	 * @param functionContext contains an argument String[] where the first contains the regionName
	 *
	 */
	@Override
	public void execute(FunctionContext<Object> functionContext)
	{
		try
		{
			String[] args = (String[])functionContext.getArguments();
			if(args == null || args.length == 0)
				throw new IllegalArgumentException("region argument required");

			String regionName = args[0];

			if(regionName == null || regionName.length() == 0)
				throw new IllegalArgumentException("region name argument required");

			Region<Serializable,Object> region = CacheFactory.getAnyInstance().getRegion(regionName);

			if(region == null)
				throw new IllegalArgumentException("region:"+regionName+" not found");

			functionContext.getResultSender().lastResult(buildCheckSumMap(region));
		}
		catch (Exception e)
		{
			String stack = Debugger.stackTrace(e);
			LogManager.getLogger(getClass()).error(stack);
			throw new FunctionException(stack);
		}
	}// --------------------------------------------------------
	/**
	 * Build check sum map
	 * @param region the region to build the information
	 * @return the map of the keys/checksums
	 */
	HashMap<Serializable,BigInteger> buildCheckSumMap(Region<Serializable,Object> region)
	{

		if(region.getAttributes().getDataPolicy().withPartitioning())
		{
			region = PartitionRegionHelper.getLocalData(region);
		}

		Set<Serializable> keySet = region.keySet();

		if(keySet == null || keySet.isEmpty())
			return null;

		HashMap<Serializable,BigInteger> regionCheckSumMap = new HashMap<Serializable,BigInteger>(keySet.size());
		Object object = null;

		Object tmp = null;
		for (Map.Entry<Serializable,Object> entry :region.entrySet())
		{
			object = entry.getValue();

			if(PdxInstance.class.isAssignableFrom(object.getClass()))
			{
				tmp = ((PdxInstance)object).getObject();

				if(Serializable.class.isAssignableFrom(tmp.getClass()))
				{
					object = tmp;
				}
				//else use PdxInstance.hashCode
			}

			if(!(PdxInstance.class.isAssignableFrom(object.getClass())))
			{
				regionCheckSumMap.put(entry.getKey(), MD.checksum(object));
			}
			else
			{
				regionCheckSumMap.put(entry.getKey(),  BigInteger.valueOf(object.hashCode()));
			}
		}

		return regionCheckSumMap;
	}// --------------------------------------------------------

	@Override
	public String getId()
	{
		return "GetEntriesChecksumFunction";
	}// --------------------------------------------------------
	@Override
	public boolean hasResult()
	{
		return true;
	}

	@Override
	public boolean isHA()
	{
		return true;
	}// --------------------------------------------------------

	@Override
	public boolean optimizeForWrite()
	{
		return false;
	}// --------------------------------------------------------

	@Override
	public void init(Properties arg0)
	{
	}// --------------------------------------------------------
}