You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by hi...@apache.org on 2016/09/13 22:44:34 UTC
[46/61] [abbrv] incubator-geode git commit: GEODE-37 change package
name from com.gemstone.gemfire (for
./geode-lucene/src/main/java/com/gemstone/gemfire)to org.apache.geode for(to
./geode-lucene/src/main/java/org/apache/geode)
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
new file mode 100755
index 0000000..200a14f
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java
@@ -0,0 +1,470 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.cache.lucene.internal.cli;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.geode.security.ResourcePermission.Operation;
+import org.apache.geode.security.ResourcePermission.Resource;
+import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
+import com.gemstone.gemfire.SystemFailure;
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.execute.Execution;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionInvocationTargetException;
+import com.gemstone.gemfire.cache.execute.ResultCollector;
+
+import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneCreateIndexFunction;
+import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneDescribeIndexFunction;
+import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneListIndexFunction;
+import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneSearchIndexFunction;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.internal.cache.execute.AbstractExecution;
+import com.gemstone.gemfire.internal.security.IntegratedSecurityService;
+import com.gemstone.gemfire.internal.security.SecurityService;
+import com.gemstone.gemfire.management.cli.CliMetaData;
+import com.gemstone.gemfire.management.cli.ConverterHint;
+import com.gemstone.gemfire.management.cli.Result;
+import com.gemstone.gemfire.management.internal.cli.CliUtil;
+import com.gemstone.gemfire.management.internal.cli.commands.AbstractCommandsSupport;
+import com.gemstone.gemfire.management.internal.cli.functions.CliFunctionResult;
+import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
+import com.gemstone.gemfire.management.internal.cli.result.CommandResult;
+import com.gemstone.gemfire.management.internal.cli.result.CommandResultException;
+import com.gemstone.gemfire.management.internal.cli.result.ResultBuilder;
+import com.gemstone.gemfire.management.internal.cli.result.TabularResultData;
+import com.gemstone.gemfire.management.internal.cli.shell.Gfsh;
+import com.gemstone.gemfire.management.internal.configuration.domain.XmlEntity;
+import com.gemstone.gemfire.management.internal.security.ResourceOperation;
+
+/**
+ * The LuceneIndexCommands class encapsulates all Geode shell (Gfsh) commands related to Lucene indexes defined in Geode.
+ * </p>
+ * @see AbstractCommandsSupport
+ * @see LuceneIndexDetails
+ * @see LuceneListIndexFunction
+ */
+@SuppressWarnings("unused")
+public class LuceneIndexCommands extends AbstractCommandsSupport {
+ private static final LuceneCreateIndexFunction createIndexFunction = new LuceneCreateIndexFunction();
+ private static final LuceneDescribeIndexFunction describeIndexFunction = new LuceneDescribeIndexFunction();
+ private static final LuceneSearchIndexFunction searchIndexFunction = new LuceneSearchIndexFunction();
+ private List<LuceneSearchResults> searchResults=null;
+
+ private SecurityService securityService = IntegratedSecurityService.getSecurityService();
+
+ @CliCommand(value = LuceneCliStrings.LUCENE_LIST_INDEX, help = LuceneCliStrings.LUCENE_LIST_INDEX__HELP)
+ @CliMetaData(shellOnly = false, relatedTopic={CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA })
+ @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
+ public Result listIndex(
+ @CliOption(key = LuceneCliStrings.LUCENE_LIST_INDEX__STATS,
+ mandatory=false,
+ specifiedDefaultValue = "true",
+ unspecifiedDefaultValue = "false",
+ help = LuceneCliStrings.LUCENE_LIST_INDEX__STATS__HELP) final boolean stats) {
+
+ try {
+ return toTabularResult(getIndexListing(),stats);
+ }
+ catch (FunctionInvocationTargetException ignore) {
+ return ResultBuilder.createGemFireErrorResult(CliStrings.format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN,
+ LuceneCliStrings.LUCENE_LIST_INDEX));
+ }
+ catch (VirtualMachineError e) {
+ SystemFailure.initiateFailure(e);
+ throw e;
+ }
+ catch (Throwable t) {
+ SystemFailure.checkFailure();
+ getCache().getLogger().info(t);
+ return ResultBuilder.createGemFireErrorResult(String.format(LuceneCliStrings.LUCENE_LIST_INDEX__ERROR_MESSAGE,
+ toString(t, isDebugging())));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<LuceneIndexDetails> getIndexListing() {
+ final Execution functionExecutor = getMembersFunctionExecutor(getMembers(getCache()));
+
+ if (functionExecutor instanceof AbstractExecution) {
+ ((AbstractExecution) functionExecutor).setIgnoreDepartedMembers(true);
+ }
+
+ final ResultCollector resultsCollector = functionExecutor.execute(new LuceneListIndexFunction());
+ final List<Set<LuceneIndexDetails>> results = (List<Set<LuceneIndexDetails>>) resultsCollector.getResult();
+
+ return results.stream()
+ .flatMap(set -> set.stream())
+ .sorted()
+ .collect(Collectors.toList());
+ }
+
+ protected Result toTabularResult(final List<LuceneIndexDetails> indexDetailsList, boolean stats) {
+ if (!indexDetailsList.isEmpty()) {
+ final TabularResultData indexData = ResultBuilder.createTabularResultData();
+
+ for (final LuceneIndexDetails indexDetails : indexDetailsList) {
+ indexData.accumulate("Index Name", indexDetails.getIndexName());
+ indexData.accumulate("Region Path", indexDetails.getRegionPath());
+ indexData.accumulate("Indexed Fields", indexDetails.getSearchableFieldNamesString());
+ indexData.accumulate("Field Analyzer", indexDetails.getFieldAnalyzersString());
+ indexData.accumulate("Status", indexDetails.getInitialized() == true ? "Initialized" : "Defined");
+
+ if (stats == true) {
+ if (!indexDetails.getInitialized()) {
+ indexData.accumulate("Query Executions", "NA");
+ indexData.accumulate("Updates", "NA");
+ indexData.accumulate("Commits", "NA");
+ indexData.accumulate("Documents", "NA");
+ }
+ else {
+ indexData.accumulate("Query Executions", indexDetails.getIndexStats().get("queryExecutions"));
+ indexData.accumulate("Updates", indexDetails.getIndexStats().get("updates"));
+ indexData.accumulate("Commits", indexDetails.getIndexStats().get("commits"));
+ indexData.accumulate("Documents", indexDetails.getIndexStats().get("documents"));
+ }
+ }
+ }
+ return ResultBuilder.buildResult(indexData);
+ }
+ else {
+ return ResultBuilder.createInfoResult(LuceneCliStrings.LUCENE_LIST_INDEX__INDEXES_NOT_FOUND_MESSAGE);
+ }
+ }
+
+ @CliCommand(value = LuceneCliStrings.LUCENE_CREATE_INDEX, help = LuceneCliStrings.LUCENE_CREATE_INDEX__HELP)
+ @CliMetaData(shellOnly = false, relatedTopic={CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA }, writesToSharedConfiguration=true)
+ //TODO : Add optionContext for indexName
+ public Result createIndex(
+ @CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME,
+ mandatory=true,
+ help = LuceneCliStrings.LUCENE_CREATE_INDEX__NAME__HELP) final String indexName,
+
+ @CliOption (key = LuceneCliStrings.LUCENE__REGION_PATH,
+ mandatory = true,
+ optionContext = ConverterHint.REGIONPATH,
+ help = LuceneCliStrings.LUCENE_CREATE_INDEX__REGION_HELP) final String regionPath,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD,
+ mandatory = true,
+ help = LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD_HELP)
+ @CliMetaData (valueSeparator = ",") final String[] fields,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER,
+ mandatory = false,
+ unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE,
+ help = LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER_HELP)
+ @CliMetaData (valueSeparator = ",") final String[] analyzers,
+
+ @CliOption (key = LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP,
+ optionContext = ConverterHint.MEMBERGROUP,
+ unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE,
+ help = LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP__HELP)
+ @CliMetaData (valueSeparator = ",") final String[] groups) {
+
+ Result result = null;
+ XmlEntity xmlEntity = null;
+
+ this.securityService.authorizeRegionManage(regionPath);
+ try {
+ final Cache cache = getCache();
+ LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexName, regionPath, fields, analyzers);
+ final ResultCollector<?, ?> rc = this.executeFunctionOnGroups(createIndexFunction, groups, indexInfo);
+ final List<CliFunctionResult> funcResults = (List<CliFunctionResult>) rc.getResult();
+
+ final TabularResultData tabularResult = ResultBuilder.createTabularResultData();
+ for (final CliFunctionResult cliFunctionResult : funcResults) {
+ tabularResult.accumulate("Member",cliFunctionResult.getMemberIdOrName());
+
+ if (cliFunctionResult.isSuccessful()) {
+ tabularResult.accumulate("Status","Successfully created lucene index");
+// if (xmlEntity == null) {
+// xmlEntity = cliFunctionResult.getXmlEntity();
+// }
+ }
+ else {
+ tabularResult.accumulate("Status","Failed: "+cliFunctionResult.getMessage());
+ }
+ }
+ result = ResultBuilder.buildResult(tabularResult);
+ }
+ catch (CommandResultException crex) {
+ result = crex.getResult();
+ } catch (Exception e) {
+ result = ResultBuilder.createGemFireErrorResult(e.getMessage());
+ }
+// TODO - store in cluster config
+// if (xmlEntity != null) {
+// result.setCommandPersisted((new SharedConfigurationWriter()).addXmlEntity(xmlEntity, groups));
+// }
+
+ return result;
+ }
+
+ @CliCommand(value = LuceneCliStrings.LUCENE_DESCRIBE_INDEX, help = LuceneCliStrings.LUCENE_DESCRIBE_INDEX__HELP)
+ @CliMetaData(shellOnly = false, relatedTopic={CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA })
+ @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
+ public Result describeIndex(
+ @CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME,
+ mandatory=true,
+ help = LuceneCliStrings.LUCENE_DESCRIBE_INDEX__NAME__HELP) final String indexName,
+
+ @CliOption (key = LuceneCliStrings.LUCENE__REGION_PATH,
+ mandatory = true,
+ optionContext = ConverterHint.REGIONPATH,
+ help = LuceneCliStrings.LUCENE_DESCRIBE_INDEX__REGION_HELP) final String regionPath) {
+ try {
+ LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexName, regionPath);
+ return toTabularResult(getIndexDetails(indexInfo),true);
+ }
+ catch (FunctionInvocationTargetException ignore) {
+ return ResultBuilder.createGemFireErrorResult(CliStrings.format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN,
+ LuceneCliStrings.LUCENE_DESCRIBE_INDEX));
+ }
+ catch (VirtualMachineError e) {
+ SystemFailure.initiateFailure(e);
+ throw e;
+ }
+ catch (IllegalArgumentException e) {
+ return ResultBuilder.createInfoResult(e.getMessage());
+ }
+ catch (Throwable t) {
+ SystemFailure.checkFailure();
+ getCache().getLogger().info(t);
+ return ResultBuilder.createGemFireErrorResult(String.format(LuceneCliStrings.LUCENE_DESCRIBE_INDEX__ERROR_MESSAGE,
+ toString(t, isDebugging())));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<LuceneIndexDetails> getIndexDetails(LuceneIndexInfo indexInfo) throws Exception {
+ this.securityService.authorizeRegionManage(indexInfo.getRegionPath());
+ final ResultCollector<?, ?> rc = this.executeFunctionOnGroups(describeIndexFunction, new String[] {}, indexInfo);
+ final List<LuceneIndexDetails> funcResults = (List<LuceneIndexDetails>) rc.getResult();
+ return funcResults.stream().filter(indexDetails -> indexDetails != null).collect(Collectors.toList());
+ }
+
+ @CliCommand(value = LuceneCliStrings.LUCENE_SEARCH_INDEX, help = LuceneCliStrings.LUCENE_SEARCH_INDEX__HELP)
+ @CliMetaData(shellOnly = false, relatedTopic = { CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA })
+ @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
+ public Result searchIndex(
+ @CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME,
+ mandatory = true,
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__NAME__HELP) final String indexName,
+
+ @CliOption(key = LuceneCliStrings.LUCENE__REGION_PATH,
+ mandatory = true,
+ optionContext = ConverterHint.REGIONPATH,
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__REGION_HELP) final String regionPath,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_SEARCH_INDEX__QUERY_STRING,
+ mandatory = true,
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__QUERY_STRING__HELP) final String queryString,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_SEARCH_INDEX__DEFAULT_FIELD,
+ mandatory = true,
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__DEFAULT_FIELD__HELP) final String defaultField,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_SEARCH_INDEX__LIMIT,
+ mandatory = false,
+ unspecifiedDefaultValue = "-1",
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__LIMIT__HELP) final int limit,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_SEARCH_INDEX__PAGE_SIZE,
+ mandatory = false,
+ unspecifiedDefaultValue = "-1",
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__PAGE_SIZE__HELP) int pageSize,
+
+ @CliOption(key = LuceneCliStrings.LUCENE_SEARCH_INDEX__KEYSONLY,
+ mandatory = false,
+ unspecifiedDefaultValue = "false",
+ help = LuceneCliStrings.LUCENE_SEARCH_INDEX__KEYSONLY__HELP) boolean keysOnly)
+ {
+ try {
+ LuceneQueryInfo queryInfo = new LuceneQueryInfo(indexName, regionPath, queryString, defaultField, limit, keysOnly);
+ if (pageSize == -1) {
+ pageSize = Integer.MAX_VALUE;
+ }
+ searchResults = getSearchResults(queryInfo);
+ return displayResults(pageSize, keysOnly);
+ }
+ catch (FunctionInvocationTargetException ignore) {
+ return ResultBuilder.createGemFireErrorResult(CliStrings.format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN,
+ LuceneCliStrings.LUCENE_SEARCH_INDEX));
+ }
+ catch (VirtualMachineError e) {
+ SystemFailure.initiateFailure(e);
+ throw e;
+ }
+ catch (IllegalArgumentException e) {
+ return ResultBuilder.createInfoResult(e.getMessage());
+ }
+ catch (Throwable t) {
+ SystemFailure.checkFailure();
+ getCache().getLogger().info(t);
+ return ResultBuilder.createGemFireErrorResult(String.format(LuceneCliStrings.LUCENE_SEARCH_INDEX__ERROR_MESSAGE,
+ toString(t, isDebugging())));
+ }
+ }
+
+ private Result displayResults(int pageSize, boolean keysOnly) throws Exception {
+ if (searchResults.size() == 0) {
+ return ResultBuilder.createInfoResult(LuceneCliStrings.LUCENE_SEARCH_INDEX__NO_RESULTS_MESSAGE);
+ }
+
+ Gfsh gfsh = initGfsh();
+ boolean pagination = searchResults.size() > pageSize;
+ int fromIndex = 0;
+ int toIndex = pageSize < searchResults.size() ? pageSize : searchResults.size();
+ int currentPage = 1;
+ int totalPages = (int) Math.ceil((float) searchResults.size() / pageSize);
+ boolean skipDisplay = false;
+ String step = null;
+ do {
+
+ if (!skipDisplay) {
+ CommandResult commandResult = (CommandResult) getResults(fromIndex, toIndex, keysOnly);
+ if (!pagination) {
+ return commandResult;
+ }
+ Gfsh.println();
+ while (commandResult.hasNextLine()) {
+ gfsh.printAsInfo(commandResult.nextLine());
+ }
+ gfsh.printAsInfo("\t\tPage " + currentPage + " of " + totalPages);
+ String message = ("Press n to move to next page, q to quit and p to previous page : ");
+ step = gfsh.interact(message);
+ }
+
+ switch (step) {
+ case "n":
+ {
+ if (currentPage == totalPages) {
+ gfsh.printAsInfo("No more results to display.");
+ step = gfsh.interact("Press p to move to last page and q to quit.");
+ skipDisplay = true;
+ continue;
+ }
+
+ if(skipDisplay) {
+ skipDisplay=false;
+ }
+ else {
+ currentPage++;
+ int current = fromIndex;
+ fromIndex = toIndex;
+ toIndex = (pageSize + fromIndex >= searchResults.size()) ? searchResults.size() : pageSize + fromIndex;
+ }
+ break;
+ }
+ case "p": {
+ if (currentPage == 1) {
+ gfsh.printAsInfo("At the top of the search results.");
+ step = gfsh.interact("Press n to move to the first page and q to quit.");
+ skipDisplay=true;
+ continue;
+ }
+
+ if (skipDisplay) {
+ skipDisplay = false;
+ }
+ else {
+ currentPage--;
+ int current = fromIndex;
+ toIndex = fromIndex;
+ fromIndex = current - pageSize <= 0 ? 0 : current - pageSize;
+ }
+ break;
+ }
+ case "q":
+ return ResultBuilder.createInfoResult("Search complete.");
+ default:
+ Gfsh.println("Invalid option");
+ break;
+ }
+ } while(true);
+ }
+
+ protected Gfsh initGfsh() {
+ return Gfsh.getCurrentInstance();
+ }
+
+ private List<LuceneSearchResults> getSearchResults(final LuceneQueryInfo queryInfo) throws Exception {
+ securityService.authorizeRegionManage(queryInfo.getRegionPath());
+
+ final String[] groups = {};
+ final ResultCollector<?, ?> rc = this.executeSearch(queryInfo);
+ final List<Set<LuceneSearchResults>> functionResults = (List<Set<LuceneSearchResults>>) rc.getResult();
+
+ return functionResults.stream()
+ .flatMap(set -> set.stream())
+ .sorted()
+ .collect(Collectors.toList());
+ }
+
+ private Result getResults(int fromIndex, int toIndex, boolean keysonly) throws Exception {
+ final TabularResultData data = ResultBuilder.createTabularResultData();
+ for (int i = fromIndex; i < toIndex; i++) {
+ if (!searchResults.get(i).getExeptionFlag()) {
+ data.accumulate("key", searchResults.get(i).getKey());
+ if (!keysonly) {
+ data.accumulate("value", searchResults.get(i).getValue());
+ data.accumulate("score", searchResults.get(i).getScore());
+ }
+ }
+ else {
+ throw new Exception(searchResults.get(i).getExceptionMessage());
+ }
+ }
+ return ResultBuilder.buildResult(data);
+ }
+
+ protected ResultCollector<?, ?> executeFunctionOnGroups(FunctionAdapter function,
+ String[] groups,
+ final LuceneIndexInfo indexInfo) throws IllegalArgumentException, CommandResultException
+ {
+ final Set<DistributedMember> targetMembers;
+ if (function != createIndexFunction) {
+ targetMembers = CliUtil.getMembersForeRegionViaFunction(getCache(), indexInfo.getRegionPath());
+ if (targetMembers.isEmpty()) {
+ throw new IllegalArgumentException("Region not found.");
+ }
+ }
+ else {
+ targetMembers = CliUtil.findAllMatchingMembers(groups, null);
+ }
+ return CliUtil.executeFunction(function, indexInfo, targetMembers);
+ }
+
+ protected ResultCollector<?, ?> executeSearch(final LuceneQueryInfo queryInfo) throws Exception {
+ final Set<DistributedMember> targetMembers = CliUtil.getMembersForeRegionViaFunction(getCache(),queryInfo.getRegionPath());
+ if (targetMembers.isEmpty())
+ throw new IllegalArgumentException("Region not found.");
+ return CliUtil.executeFunction(searchIndexFunction, queryInfo, targetMembers);
+ }
+
+ @CliAvailabilityIndicator({LuceneCliStrings.LUCENE_SEARCH_INDEX, LuceneCliStrings.LUCENE_CREATE_INDEX,
+ LuceneCliStrings.LUCENE_DESCRIBE_INDEX, LuceneCliStrings.LUCENE_LIST_INDEX})
+ public boolean indexCommandsAvailable() {
+ return (!CliUtil.isGfshVM() || (getGfsh() != null && getGfsh().isConnectedAndReady()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java
new file mode 100644
index 0000000..eaec4a0
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexDetails.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.cache.lucene.internal.cli;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexCreationProfile;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexStats;
+
+import org.apache.lucene.analysis.Analyzer;
+
+public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Serializable {
+ private static final long serialVersionUID = 1L;
+ private final String indexName;
+ private final String regionPath;
+ private final String[] searchableFieldNames;
+ private Map<String, String> fieldAnalyzers=null;
+ private final Map<String,Integer> indexStats;
+ private boolean initialized;
+
+ public LuceneIndexDetails(final String indexName, final String regionPath, final String[] searchableFieldNames, final Map<String, Analyzer> fieldAnalyzers, LuceneIndexStats indexStats, boolean initialized) {
+ this.indexName = indexName;
+ this.regionPath = regionPath;
+ this.searchableFieldNames = searchableFieldNames;
+ this.fieldAnalyzers = getFieldAnalyzerStrings(fieldAnalyzers);
+ this.indexStats=getIndexStatsMap(indexStats);
+ this.initialized = initialized;
+ }
+
+ public LuceneIndexDetails(LuceneIndexImpl index) {
+ this(index.getName(), index.getRegionPath(), index.getFieldNames(),index.getFieldAnalyzers(),index.getIndexStats(), true);
+ }
+
+ public LuceneIndexDetails(LuceneIndexCreationProfile indexProfile) {
+ this(indexProfile.getIndexName(), indexProfile.getRegionPath(), indexProfile.getFieldNames(), null, null, false);
+ this.fieldAnalyzers=getFieldAnalyzerStringsFromProfile(indexProfile.getFieldAnalyzers());
+ }
+
+ public Map<String,Integer> getIndexStats() {
+ return indexStats;
+ }
+ private Map<String,Integer> getIndexStatsMap(LuceneIndexStats indexStats) {
+ Map<String,Integer> statsMap = new HashMap<>();
+ if (indexStats==null) return statsMap;
+ statsMap.put("queryExecutions",indexStats.getQueryExecutions());
+ statsMap.put("updates",indexStats.getUpdates());
+ statsMap.put("commits",indexStats.getCommits());
+ statsMap.put("documents",indexStats.getDocuments());
+ return statsMap;
+ }
+
+ public String getIndexStatsString() {
+ return indexStats.toString();
+ }
+
+ private Map<String, String> getFieldAnalyzerStrings(Map<String, Analyzer> fieldAnalyzers) {
+ if(fieldAnalyzers == null) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, String> results = new HashMap<>();
+
+ for (Entry<String, Analyzer> entry : fieldAnalyzers.entrySet()) {
+ final Analyzer analyzer = entry.getValue();
+ if(analyzer != null) {
+ results.put(entry.getKey(), analyzer.getClass().getSimpleName());
+ }
+ }
+ return results;
+ }
+
+ private Map<String, String> getFieldAnalyzerStringsFromProfile(Map<String, String> fieldAnalyzers) {
+ if(fieldAnalyzers == null) {
+ return Collections.emptyMap();
+
+ }
+
+ Map<String, String> results = new HashMap<>();
+
+ for (Entry<String, String> entry : fieldAnalyzers.entrySet()) {
+ final String analyzer = entry.getValue();
+ if(analyzer != null) {
+ results.put(entry.getKey(), analyzer);
+ }
+ }
+ return results;
+ }
+
+ public String getSearchableFieldNamesString() {
+ return Arrays.asList(searchableFieldNames).toString();
+ }
+
+
+ public String getFieldAnalyzersString() {
+ return fieldAnalyzers.toString();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer buffer = new StringBuffer();
+ buffer.append("{\n\tIndex Name = "+indexName);
+ buffer.append(",\tRegion Path = "+regionPath);
+ buffer.append(",\tIndexed Fields = "+getSearchableFieldNamesString());
+ buffer.append(",\tField Analyzer = "+getFieldAnalyzersString());
+ buffer.append(",\tStatus =\n\t"+ getInitialized());
+ buffer.append(",\tIndex Statistics =\n\t"+getIndexStatsString());
+ buffer.append("\n}\n");
+ return buffer.toString();
+ }
+
+ public boolean getInitialized() {
+ return initialized;
+ }
+
+ public String getIndexName() {
+ return indexName;
+ }
+
+ public String getRegionPath() {
+ return regionPath;
+ }
+
+ private static <T extends Comparable<T>> int compare(final T obj1, final T obj2) {
+ return (obj1 == null && obj2 == null ? 0 : (obj1 == null ? 1 : (obj2 == null ? -1 : obj1.compareTo(obj2))));
+ }
+
+ @Override
+ public int compareTo(final LuceneIndexDetails indexDetails) {
+ int comparisonValue = compare(getIndexName(), indexDetails.getIndexName());
+ return (comparisonValue != 0 ? comparisonValue : compare(getRegionPath(), indexDetails.getRegionPath()));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java
new file mode 100644
index 0000000..3df292c
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.cache.lucene.internal.cli;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl;
+
+import org.apache.lucene.analysis.Analyzer;
+
+public class LuceneIndexInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String indexName;
+ private final String regionPath;
+ private final String[] searchableFieldNames;
+ private final String[] fieldAnalyzers;
+
+ public LuceneIndexInfo(final String indexName, final String regionPath, final String[] searchableFieldNames, String[] fieldAnalyzers) {
+ this.indexName = indexName;
+ this.regionPath = regionPath;
+ this.searchableFieldNames = searchableFieldNames;
+ this.fieldAnalyzers = fieldAnalyzers;
+ }
+
+ public LuceneIndexInfo(final String indexName, final String regionPath) {
+ this(indexName,regionPath,null,null);
+ }
+
+ public String getIndexName() {
+ return indexName;
+ }
+
+ public String getRegionPath() {
+ return regionPath;
+ }
+
+ public String[] getSearchableFieldNames() {
+ return searchableFieldNames;
+ }
+
+ public String[] getFieldAnalyzers() {
+ return fieldAnalyzers;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java
new file mode 100644
index 0000000..0dbe2fe
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneQueryInfo.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.cli;
+
+import java.io.Serializable;
+
+import com.gemstone.gemfire.cache.lucene.LuceneQueryFactory;
+
+public class LuceneQueryInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private String indexName;
+ private String regionPath;
+ private String queryString;
+ private String defaultField;
+ private int limit;
+ private boolean keysOnly;
+
+ public LuceneQueryInfo(final String indexName,
+ final String regionPath,
+ final String queryString,
+ final String defaultField,
+ final int limit,
+ final boolean keysOnly)
+ {
+ this.indexName = indexName;
+ this.regionPath = regionPath;
+ this.queryString = queryString;
+ this.defaultField = defaultField;
+ this.limit = limit;
+ this.keysOnly = keysOnly;
+ }
+
+ public String getIndexName() {
+ return indexName;
+ }
+
+ public String getRegionPath() {
+ return regionPath;
+ }
+
+ public String getQueryString() {
+ return queryString;
+ }
+
+ public String getDefaultField() {
+ return defaultField;
+ }
+
+ public int getLimit() {
+ if (limit == -1) return LuceneQueryFactory.DEFAULT_LIMIT;
+ else return limit; }
+
+ public boolean getKeysOnly() { return keysOnly; }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneSearchResults.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneSearchResults.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneSearchResults.java
new file mode 100644
index 0000000..9f03873
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneSearchResults.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.cli;
+
+import java.io.Serializable;
+
+public class LuceneSearchResults<K,V> implements Comparable<LuceneSearchResults>, Serializable {
+
+ private String key;
+ private String value;
+ private float score;
+ private boolean exceptionFlag = false;
+ private String exceptionMessage;
+
+
+ public LuceneSearchResults(final String key, final String value, final float score) {
+ this.key = key;
+ this.value = value;
+ this.score = score;
+ }
+
+ public LuceneSearchResults(final String key) {
+ this.key = key;
+ }
+
+ public LuceneSearchResults(final boolean exceptionFlag, final String exceptionMessage) {
+ this.exceptionFlag=exceptionFlag;
+ this.exceptionMessage=exceptionMessage;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public float getScore() {
+ return score;
+ }
+
+ @Override
+ public int compareTo(final LuceneSearchResults searchResults) {
+ return Float.compare(getScore(),searchResults.getScore());
+ }
+
+ public boolean getExeptionFlag() { return exceptionFlag; }
+
+ public String getExceptionMessage() { return exceptionMessage; }
+
+ @Override public String toString() {
+ return "LuceneSearchResults{" +
+ "key='" + key + '\'' +
+ ", value='" + value + '\'' +
+ ", score=" + score +
+ ", exceptionFlag=" + exceptionFlag +
+ ", exceptionMessage='" + exceptionMessage + '\'' +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
new file mode 100644
index 0000000..cb45f7b
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneCreateIndexFunction.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.cli.functions;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.lucene.LuceneService;
+import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneCliStrings;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexDetails;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexInfo;
+import com.gemstone.gemfire.internal.InternalEntity;
+import com.gemstone.gemfire.internal.lang.StringUtils;
+import com.gemstone.gemfire.management.internal.cli.CliUtil;
+import com.gemstone.gemfire.management.internal.cli.functions.CliFunctionResult;
+import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
+import com.gemstone.gemfire.management.internal.configuration.domain.XmlEntity;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+
+/**
+ * The LuceneCreateIndexFunction class is a function used to create Lucene indexes.
+ * </p>
+ * @see Cache
+ * @see com.gemstone.gemfire.cache.execute.Function
+ * @see FunctionAdapter
+ * @see FunctionContext
+ * @see InternalEntity
+ * @see LuceneIndexDetails
+ */
+@SuppressWarnings("unused")
+public class LuceneCreateIndexFunction extends FunctionAdapter implements InternalEntity {
+
+ protected Cache getCache() {
+ return CacheFactory.getAnyInstance();
+ }
+
+ public String getId() {
+ return LuceneListIndexFunction.class.getName();
+ }
+
+ public void execute(final FunctionContext context) {
+ String memberId = null;
+ try {
+ final LuceneIndexInfo indexInfo = (LuceneIndexInfo) context.getArguments();
+ final Cache cache = getCache();
+ memberId = cache.getDistributedSystem().getDistributedMember().getId();
+ LuceneService service = LuceneServiceProvider.get(cache);
+
+ String[] fields = indexInfo.getSearchableFieldNames();
+ String[] analyzerName = indexInfo.getFieldAnalyzers();
+
+ if (analyzerName == null || analyzerName.length == 0) {
+ service.createIndex(indexInfo.getIndexName(), indexInfo.getRegionPath(), fields);
+ }
+ else {
+ if (analyzerName.length != fields.length) throw new Exception("Mismatch in lengths of fields and analyzers");
+ Map<String, Analyzer> fieldAnalyzer = new HashMap<>();
+ for (int i = 0; i < fields.length; i++) {
+ Analyzer analyzer = toAnalyzer(analyzerName[i]);
+ fieldAnalyzer.put(fields[i], analyzer);
+ }
+ service.createIndex(indexInfo.getIndexName(), indexInfo.getRegionPath(), fieldAnalyzer);
+ }
+
+ //TODO - update cluster configuration by returning a valid XmlEntity
+ XmlEntity xmlEntity = null;
+ context.getResultSender().lastResult(new CliFunctionResult(memberId, xmlEntity));
+ }
+ catch (Exception e) {
+ String exceptionMessage = CliStrings.format(CliStrings.EXCEPTION_CLASS_AND_MESSAGE, e.getClass().getName(),
+ e.getMessage());
+ context.getResultSender().lastResult(new CliFunctionResult(memberId, e, e.getMessage()));
+ }
+ }
+
+ private Analyzer toAnalyzer(String className)
+ {
+ if (className==null)
+ className=StandardAnalyzer.class.getCanonicalName();
+ else if (StringUtils.trim(className).equals("") | StringUtils.trim(className).equals("null") )
+ className = StandardAnalyzer.class.getCanonicalName();
+
+ Class<? extends Analyzer> clazz = CliUtil.forName(className, LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER);
+ return CliUtil.newInstance(clazz, LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java
new file mode 100755
index 0000000..015828a
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.cli.functions;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.lucene.LuceneIndex;
+import com.gemstone.gemfire.cache.lucene.LuceneService;
+import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexCreationProfile;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneServiceImpl;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexDetails;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexInfo;
+import com.gemstone.gemfire.internal.InternalEntity;
+
+/**
+ * The LuceneDescribeIndexFunction class is a function used to collect the information on a particular lucene index.
+ * </p>
+ * @see Cache
+ * @see com.gemstone.gemfire.cache.execute.Function
+ * @see FunctionAdapter
+ * @see FunctionContext
+ * @see InternalEntity
+ * @see LuceneIndexDetails
+ * @see LuceneIndexInfo
+ */
+@SuppressWarnings("unused")
+public class LuceneDescribeIndexFunction extends FunctionAdapter implements InternalEntity {
+
+ protected Cache getCache() {
+ return CacheFactory.getAnyInstance();
+ }
+
+ public String getId() {
+ return LuceneDescribeIndexFunction.class.getName();
+ }
+
+ public void execute(final FunctionContext context) {
+ LuceneIndexDetails result = null;
+
+ final Cache cache = getCache();
+ final LuceneIndexInfo indexInfo = (LuceneIndexInfo) context.getArguments();
+ LuceneServiceImpl service = (LuceneServiceImpl) LuceneServiceProvider.get(cache);
+ LuceneIndex index = service.getIndex(indexInfo.getIndexName(), indexInfo.getRegionPath());
+ LuceneIndexCreationProfile profile = service.getDefinedIndex(indexInfo.getIndexName(),indexInfo.getRegionPath());
+ if (index != null) {
+ result = new LuceneIndexDetails((LuceneIndexImpl) index);
+ } else if (profile != null) {
+ result = new LuceneIndexDetails(profile);
+ }
+ context.getResultSender().lastResult(result);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneListIndexFunction.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneListIndexFunction.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneListIndexFunction.java
new file mode 100755
index 0000000..bb74410
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneListIndexFunction.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.cli.functions;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.lucene.LuceneIndex;
+import com.gemstone.gemfire.cache.lucene.LuceneService;
+import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexCreationProfile;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneServiceImpl;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexDetails;
+import com.gemstone.gemfire.internal.InternalEntity;
+
+/**
+ * The LuceneListIndexFunction class is a function used to collect the information on all lucene indexes in
+ * the entire Cache.
+ * </p>
+ * @see Cache
+ * @see com.gemstone.gemfire.cache.execute.Function
+ * @see FunctionAdapter
+ * @see FunctionContext
+ * @see InternalEntity
+ * @see LuceneIndexDetails
+ */
+@SuppressWarnings("unused")
+public class LuceneListIndexFunction extends FunctionAdapter implements InternalEntity {
+
+ protected Cache getCache() {
+ return CacheFactory.getAnyInstance();
+ }
+
+ public String getId() {
+ return LuceneListIndexFunction.class.getName();
+ }
+
+ public void execute(final FunctionContext context) {
+ final Set<LuceneIndexDetails> indexDetailsSet = new HashSet<>();
+ final Cache cache = getCache();
+ LuceneServiceImpl service = (LuceneServiceImpl) LuceneServiceProvider.get(cache);
+ for (LuceneIndex index : service.getAllIndexes()) {
+ indexDetailsSet.add(new LuceneIndexDetails((LuceneIndexImpl) index));
+ }
+
+ for(LuceneIndexCreationProfile profile : service.getAllDefinedIndexes()) {
+ indexDetailsSet.add(new LuceneIndexDetails(profile));
+ }
+ context.getResultSender().lastResult(indexDetailsSet);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneSearchIndexFunction.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneSearchIndexFunction.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneSearchIndexFunction.java
new file mode 100755
index 0000000..234583c
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneSearchIndexFunction.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.cli.functions;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.lucene.LuceneQuery;
+import com.gemstone.gemfire.cache.lucene.LuceneQueryException;
+import com.gemstone.gemfire.cache.lucene.LuceneResultStruct;
+import com.gemstone.gemfire.cache.lucene.LuceneService;
+import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider;
+import com.gemstone.gemfire.cache.lucene.PageableLuceneQueryResults;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexDetails;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexInfo;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneQueryInfo;
+import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneSearchResults;
+import com.gemstone.gemfire.cache.query.RegionNotFoundException;
+import com.gemstone.gemfire.internal.InternalEntity;
+
+/**
+ * The LuceneSearchIndexFunction class is a function used to collect the information on a particular lucene index.
+ * </p>
+ * @see Cache
+ * @see com.gemstone.gemfire.cache.execute.Function
+ * @see FunctionAdapter
+ * @see FunctionContext
+ * @see InternalEntity
+ * @see LuceneIndexDetails
+ * @see LuceneIndexInfo
+ */
+@SuppressWarnings("unused")
+public class LuceneSearchIndexFunction<K, V> extends FunctionAdapter implements InternalEntity {
+
+ protected Cache getCache() {
+ return CacheFactory.getAnyInstance();
+ }
+
+ public String getId() {
+ return LuceneSearchIndexFunction.class.getName();
+ }
+
+ public void execute(final FunctionContext context) {
+ Set<LuceneSearchResults> result = new HashSet<>();
+ final Cache cache = getCache();
+ final LuceneQueryInfo queryInfo = (LuceneQueryInfo) context.getArguments();
+
+ LuceneService luceneService = LuceneServiceProvider.get(getCache());
+ try {
+ if (luceneService.getIndex(queryInfo.getIndexName(), queryInfo.getRegionPath()) == null) {
+ throw new Exception("Index " + queryInfo.getIndexName() + " not found on region " + queryInfo.getRegionPath());
+ }
+ final LuceneQuery<K, V> query = luceneService.createLuceneQueryFactory()
+ .setResultLimit(queryInfo.getLimit())
+ .create(queryInfo.getIndexName(), queryInfo.getRegionPath(), queryInfo.getQueryString(),
+ queryInfo.getDefaultField());
+ if (queryInfo.getKeysOnly()) {
+ query.findKeys().forEach(key -> result.add(new LuceneSearchResults(key.toString())));
+ }
+ else {
+ PageableLuceneQueryResults pageableLuceneQueryResults = query.findPages();
+ while (pageableLuceneQueryResults.hasNext()) {
+ List<LuceneResultStruct> page = pageableLuceneQueryResults.next();
+ page.stream()
+ .forEach(searchResult ->
+ result.add(
+ new LuceneSearchResults<K, V>(searchResult.getKey().toString(), searchResult.getValue().toString(),
+ searchResult.getScore())));
+ }
+ }
+ if (result != null) {
+ context.getResultSender().lastResult(result);
+ }
+ }
+ catch (LuceneQueryException e) {
+ result.add(new LuceneSearchResults(true, e.getRootCause().getMessage()));
+ context.getResultSender().lastResult(result);
+ }
+ catch (Exception e) {
+ result.add(new LuceneSearchResults(true, e.getMessage()));
+ context.getResultSender().lastResult(result);
+ }
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/DumpDirectoryFiles.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/DumpDirectoryFiles.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/DumpDirectoryFiles.java
new file mode 100644
index 0000000..0d18144
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/DumpDirectoryFiles.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.directory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.execute.Function;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.execute.FunctionException;
+import com.gemstone.gemfire.cache.execute.RegionFunctionContext;
+import com.gemstone.gemfire.cache.execute.ResultSender;
+import com.gemstone.gemfire.cache.lucene.LuceneQueryProvider;
+import com.gemstone.gemfire.cache.lucene.LuceneService;
+import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider;
+import com.gemstone.gemfire.cache.lucene.internal.InternalLuceneIndex;
+import com.gemstone.gemfire.cache.lucene.internal.LuceneServiceImpl;
+import com.gemstone.gemfire.cache.lucene.internal.distributed.CollectorManager;
+import com.gemstone.gemfire.cache.lucene.internal.distributed.LuceneFunctionContext;
+import com.gemstone.gemfire.cache.lucene.internal.distributed.TopEntriesCollector;
+import com.gemstone.gemfire.cache.lucene.internal.distributed.TopEntriesCollectorManager;
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.FileSystem;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexRepository;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexRepositoryImpl;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexResultCollector;
+import com.gemstone.gemfire.cache.lucene.internal.repository.RepositoryManager;
+import com.gemstone.gemfire.cache.query.QueryException;
+import com.gemstone.gemfire.internal.InternalEntity;
+import com.gemstone.gemfire.internal.cache.BucketNotFoundException;
+import com.gemstone.gemfire.internal.logging.LogService;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.store.Directory;
+
+public class DumpDirectoryFiles implements Function, InternalEntity {
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger logger = LogService.getLogger();
+ public static final String ID = DumpDirectoryFiles.class.getSimpleName();
+
+ @Override
+ public void execute(FunctionContext context) {
+ RegionFunctionContext ctx = (RegionFunctionContext) context;
+
+ if(!(context.getArguments() instanceof String[])) {
+ throw new IllegalArgumentException("Arguments should be a string array");
+ }
+ String[] args = (String[]) context.getArguments();
+ if(args.length != 2) {
+ throw new IllegalArgumentException("Expected 2 arguments: exportLocation, indexName");
+ }
+
+
+ String exportLocation =args[0];
+ String indexName =args[1];
+
+ final Region<Object, Object> region = ctx.getDataSet();
+ LuceneService service = LuceneServiceProvider.get(ctx.getDataSet().getCache());
+ InternalLuceneIndex index = (InternalLuceneIndex) service.getIndex(indexName, region.getFullPath());
+ if(index == null) {
+ throw new IllegalStateException("Index not found for region " + region + " index " + indexName);
+ }
+
+ final RepositoryManager repoManager = index.getRepositoryManager();
+ try {
+ final Collection<IndexRepository> repositories = repoManager.getRepositories(ctx);
+ repositories.stream().forEach(repo -> {
+ final IndexWriter writer = repo.getWriter();
+ RegionDirectory directory = (RegionDirectory) writer.getDirectory();
+ FileSystem fs = directory.getFileSystem();
+
+ String bucketName = index.getName() + "_" + repo.getRegion().getFullPath();
+ bucketName = bucketName.replace("/", "_");
+ File bucketDirectory = new File(exportLocation, bucketName);
+ bucketDirectory.mkdirs();
+ fs.export(bucketDirectory);
+ });
+ context.getResultSender().lastResult(null);
+ }
+ catch (BucketNotFoundException e) {
+ throw new FunctionException(e);
+ }
+ }
+
+ @Override public String getId() {
+ return ID;
+ }
+
+ @Override
+ public boolean optimizeForWrite() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/FileIndexInput.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/FileIndexInput.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/FileIndexInput.java
new file mode 100644
index 0000000..9ebef51
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/FileIndexInput.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.directory;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+import org.apache.lucene.store.IndexInput;
+
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.File;
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.SeekableInputStream;
+
+final class FileIndexInput extends IndexInput {
+ private final File file;
+ SeekableInputStream in;
+ private long position;
+
+ //Used for slice operations
+ private long sliceOffset;
+ private long sliceLength;
+
+ FileIndexInput(String resourceDesc, File file) {
+ this(resourceDesc, file, 0L, file.getLength());
+ }
+
+ /**
+ * Constructor for a slice.
+ */
+ private FileIndexInput(String resourceDesc, File file, long offset, long length) {
+ super(resourceDesc);
+ this.file = file;
+ in = file.getInputStream();
+ this.sliceOffset = offset;
+ this.sliceLength = length;
+ }
+
+ @Override
+ public long length() {
+ return sliceLength;
+ }
+
+ @Override
+ public void close() throws IOException {
+ in.close();
+ }
+
+ @Override
+ public FileIndexInput clone() {
+ FileIndexInput clone = (FileIndexInput)super.clone();
+ clone.in = in.clone();
+ return clone;
+ }
+
+ @Override
+ public long getFilePointer() {
+ return position;
+ }
+
+ @Override
+ public void seek(long pos) throws IOException {
+ in.seek(pos + sliceOffset);
+ this.position = pos;
+ }
+
+ @Override
+ public IndexInput slice(String sliceDescription, long offset, long length)
+ throws IOException {
+ if(length > (this.sliceLength - offset)) {
+ throw new IllegalArgumentException("Slice length is to large. Asked for " + length + " file length is " + sliceLength + ": " + this.file.getName());
+ }
+ if(offset < 0 || offset >= this.sliceLength) {
+ throw new IllegalArgumentException("Slice offset is invalid: " + this.file.getName());
+ }
+
+ FileIndexInput result = new FileIndexInput(sliceDescription, file, sliceOffset + offset, length);
+ result.seek(0);
+ return result;
+ }
+
+ @Override
+ public byte readByte() throws IOException {
+ if(++position > sliceLength) {
+ throw new EOFException("Read past end of file " + file.getName());
+ }
+
+ int result = in.read();
+ if(result == -1) {
+ throw new EOFException("Read past end of file " + file.getName());
+ } else {
+ return (byte) result;
+ }
+ }
+
+ @Override
+ public void readBytes(byte[] b, int offset, int len) throws IOException {
+ if(len == 0) {
+ return;
+ }
+
+ if(position + len > sliceLength) {
+ throw new EOFException("Read past end of file " + file.getName());
+ }
+
+ //For the FileSystemInputStream, it will always read all bytes, up
+ //until the end of the file. So if we didn't get enough bytes, it's
+ //because we reached the end of the file.
+ int numRead = in.read(b, offset, len);
+ if(numRead < len) {
+ throw new EOFException("Read past end of file " + file.getName());
+ }
+
+ position+=len;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/RegionDirectory.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/RegionDirectory.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/RegionDirectory.java
new file mode 100644
index 0000000..45b9c97
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/RegionDirectory.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.directory;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.lucene.store.BaseDirectory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.store.OutputStreamIndexOutput;
+import org.apache.lucene.store.SingleInstanceLockFactory;
+
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.ChunkKey;
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.File;
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.FileSystem;
+import com.gemstone.gemfire.cache.lucene.internal.filesystem.FileSystemStats;
+
+/**
+ * An implementation of Directory that stores data in geode regions.
+ *
+ * Directory is an interface to file/RAM storage for lucene. This class uses
+ * the {@link FileSystem} class to store the data in the provided geode
+ * regions.
+ */
+public class RegionDirectory extends BaseDirectory {
+
+ private final FileSystem fs;
+
+ /**
+ * Create a region directory with a given file and chunk region. These regions
+ * may be bucket regions or they may be replicated regions.
+ */
+ public RegionDirectory(ConcurrentMap<String, File> fileRegion, ConcurrentMap<ChunkKey, byte[]> chunkRegion, FileSystemStats stats) {
+ super(new SingleInstanceLockFactory());
+ fs = new FileSystem(fileRegion, chunkRegion, stats);
+ }
+
+ @Override
+ public String[] listAll() throws IOException {
+ ensureOpen();
+ String[] array = fs.listFileNames().toArray(new String[]{});
+ Arrays.sort(array);
+ return array;
+ }
+
+ @Override
+ public void deleteFile(String name) throws IOException {
+ ensureOpen();
+ fs.deleteFile(name);
+ }
+
+ @Override
+ public long fileLength(String name) throws IOException {
+ ensureOpen();
+ return fs.getFile(name).getLength();
+ }
+
+ @Override
+ public IndexOutput createOutput(final String name, final IOContext context) throws IOException {
+ ensureOpen();
+ final File file = fs.createFile(name);
+ final OutputStream out = file.getOutputStream();
+
+ return new OutputStreamIndexOutput(name, name, out, 1000);
+ }
+
+ public IndexOutput createTempOutput(String prefix, String suffix, IOContext context) throws IOException {
+ String name = prefix + "_temp_" + UUID.randomUUID() + suffix;
+ final File file = fs.createTemporaryFile(name);
+ final OutputStream out = file.getOutputStream();
+
+ return new OutputStreamIndexOutput(name, name, out, 1000);
+ }
+
+ @Override
+ public void sync(Collection<String> names) throws IOException {
+ ensureOpen();
+ // Region does not need to sync to disk
+ }
+
+ @Override
+ public void renameFile(String source, String dest) throws IOException {
+ ensureOpen();
+ fs.renameFile(source, dest);
+ }
+
+ @Override
+ public IndexInput openInput(String name, IOContext context) throws IOException {
+ ensureOpen();
+ final File file = fs.getFile(name);
+
+ return new FileIndexInput(name, file);
+ }
+
+ @Override
+ public void close() throws IOException {
+ isOpen = false;
+ }
+
+ /**
+ * For testing, the file system
+ */
+ public FileSystem getFileSystem() {
+ return fs;
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/package-info.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/package-info.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/package-info.java
new file mode 100644
index 0000000..2dd0606
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/directory/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * An implementation of Lucene's {@link org.apache.lucene.store.Directory} interface that uses the filesystem
+ * API in {@link com.gemstone.gemfire.cache.lucene.internal.filesystem.FileSystem}
+ */
+package com.gemstone.gemfire.cache.lucene.internal.directory;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/CollectorManager.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/CollectorManager.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/CollectorManager.java
new file mode 100644
index 0000000..4d1d1c2
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/CollectorManager.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.distributed;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import com.gemstone.gemfire.annotations.Experimental;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexRepository;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexResultCollector;
+
+/**
+ * {@link CollectorManager}s create instances of {@link IndexResultCollector} and utility methods to aggregate results
+ * collected by individual collectors. The collectors created by this class are primarily used for collecting results
+ * from {@link IndexRepository}s. The collectors can also be used for aggregating results of other collectors of same
+ * type. Typically search result aggregation is completed in two phases. First at a member level for merging results
+ * from local buckets. And then at search coordinator level for merging results from members. Use of same collector in
+ * both phases is expected to produce deterministic merge result irrespective of the way buckets are distributed.
+ *
+ * @param <C> Type of IndexResultCollector created by this manager
+ */
+@Experimental
+public interface CollectorManager<C extends IndexResultCollector> {
+ /**
+ * @param name Name/Identifier for this collector. For e.g. region/bucketId.
+ * @return a new {@link IndexResultCollector}. This must return a different instance on
+ * each call. A new collector would be created for each bucket on a member node.
+ */
+ C newCollector(String name);
+
+ /**
+ * Reduce the results of individual collectors into a meaningful result. This method must be called after collection
+ * is finished on all provided collectors.
+ *
+ */
+ C reduce(Collection<C> results);
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/EntryScore.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/EntryScore.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/EntryScore.java
new file mode 100644
index 0000000..e891156
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/EntryScore.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.distributed;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.internal.DataSerializableFixedID;
+import com.gemstone.gemfire.internal.Version;
+
+/**
+ * Holds one entry matching search query and its metadata
+ *
+ * @param <K> the type of the key
+ */
+public class EntryScore<K> implements DataSerializableFixedID {
+ // Key of the entry matching search query
+ private K key;
+
+ // The score of this document for the query.
+ private float score;
+
+ public EntryScore() {
+ }
+
+ public EntryScore(K key, float score) {
+ this.key = key;
+ this.score = score;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public float getScore() {
+ return score;
+ }
+
+ @Override
+ public String toString() {
+ return "key=" + key + " score=" + score;
+ }
+
+ @Override
+ public Version[] getSerializationVersions() {
+ return null;
+ }
+
+ @Override
+ public int getDSFID() {
+ return LUCENE_ENTRY_SCORE;
+ }
+
+ @Override
+ public void toData(DataOutput out) throws IOException {
+ DataSerializer.writeObject(key, out);
+ out.writeFloat(score);
+ }
+
+ @Override
+ public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+ key = DataSerializer.readObject(in);
+ score = in.readFloat();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunction.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunction.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunction.java
new file mode 100644
index 0000000..3c6c0d2
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunction.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.distributed;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.lucene.search.Query;
+
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.execute.FunctionAdapter;
+import com.gemstone.gemfire.cache.execute.FunctionContext;
+import com.gemstone.gemfire.cache.execute.FunctionException;
+import com.gemstone.gemfire.cache.execute.RegionFunctionContext;
+import com.gemstone.gemfire.cache.execute.ResultSender;
+import com.gemstone.gemfire.cache.lucene.LuceneQueryException;
+import com.gemstone.gemfire.cache.lucene.LuceneQueryProvider;
+import com.gemstone.gemfire.cache.lucene.LuceneService;
+import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider;
+import com.gemstone.gemfire.cache.lucene.internal.InternalLuceneIndex;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexRepository;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexResultCollector;
+import com.gemstone.gemfire.cache.lucene.internal.repository.RepositoryManager;
+import com.gemstone.gemfire.cache.query.QueryException;
+import com.gemstone.gemfire.internal.InternalEntity;
+import com.gemstone.gemfire.internal.cache.BucketNotFoundException;
+import com.gemstone.gemfire.internal.cache.execute.BucketMovedException;
+import com.gemstone.gemfire.internal.logging.LogService;
+
+/**
+ * {@link LuceneFunction} coordinates text search on a member. It receives text search query from the coordinator
+ * and arguments like region and buckets. It invokes search on the local index and provides a result collector. The
+ * locally collected results are sent to the search coordinator.
+ */
+public class LuceneFunction extends FunctionAdapter implements InternalEntity {
+ private static final long serialVersionUID = 1L;
+ public static final String ID = LuceneFunction.class.getName();
+
+ private static final Logger logger = LogService.getLogger();
+
+ @Override
+ public void execute(FunctionContext context) {
+ RegionFunctionContext ctx = (RegionFunctionContext) context;
+ ResultSender<TopEntriesCollector> resultSender = ctx.getResultSender();
+
+ Region region = ctx.getDataSet();
+
+ LuceneFunctionContext<IndexResultCollector> searchContext = (LuceneFunctionContext) ctx.getArguments();
+ if (searchContext == null) {
+ throw new IllegalArgumentException("Missing search context");
+ }
+
+ LuceneQueryProvider queryProvider = searchContext.getQueryProvider();
+ if (queryProvider == null) {
+ throw new IllegalArgumentException("Missing query provider");
+ }
+
+ LuceneService service = LuceneServiceProvider.get(region.getCache());
+ InternalLuceneIndex index = (InternalLuceneIndex) service.getIndex(searchContext.getIndexName(), region.getFullPath());
+ RepositoryManager repoManager = index.getRepositoryManager();
+
+ Query query = null;
+ try {
+ query = queryProvider.getQuery(index);
+ } catch (LuceneQueryException e) {
+ logger.warn("", e);
+ throw new FunctionException(e);
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Executing lucene query: {}, on region {}", query, region.getFullPath());
+ }
+
+ int resultLimit = searchContext.getLimit();
+ CollectorManager manager = (searchContext == null) ? null : searchContext.getCollectorManager();
+ if (manager == null) {
+ manager = new TopEntriesCollectorManager(null, resultLimit);
+ }
+
+ Collection<IndexResultCollector> results = new ArrayList<>();
+ try {
+ Collection<IndexRepository> repositories = repoManager.getRepositories(ctx);
+ for (IndexRepository repo : repositories) {
+ IndexResultCollector collector = manager.newCollector(repo.toString());
+ logger.debug("Executing search on repo: " + repo.toString());
+ repo.query(query, resultLimit, collector);
+ results.add(collector);
+ }
+ TopEntriesCollector mergedResult = (TopEntriesCollector) manager.reduce(results);
+ resultSender.lastResult(mergedResult);
+ } catch (IOException|BucketNotFoundException e) {
+ logger.warn("", e);
+ throw new FunctionException(e);
+ }
+ }
+
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public boolean optimizeForWrite() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/05e6d966/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunctionContext.java
----------------------------------------------------------------------
diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunctionContext.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunctionContext.java
new file mode 100644
index 0000000..b0b2c60
--- /dev/null
+++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/distributed/LuceneFunctionContext.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.gemstone.gemfire.cache.lucene.internal.distributed;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import com.gemstone.gemfire.DataSerializer;
+import com.gemstone.gemfire.cache.lucene.LuceneQueryFactory;
+import com.gemstone.gemfire.cache.lucene.LuceneQueryProvider;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexRepository;
+import com.gemstone.gemfire.cache.lucene.internal.repository.IndexResultCollector;
+import com.gemstone.gemfire.internal.DataSerializableFixedID;
+import com.gemstone.gemfire.internal.Version;
+
+/**
+ * Contains function arguments for text / lucene search
+ */
+public class LuceneFunctionContext<C extends IndexResultCollector> implements DataSerializableFixedID {
+ private CollectorManager<C> manager;
+ private int limit;
+ private LuceneQueryProvider queryProvider;
+ private String indexName;
+
+ public LuceneFunctionContext() {
+ this(null, null, null);
+ }
+
+ public LuceneFunctionContext(LuceneQueryProvider provider, String indexName) {
+ this(provider, indexName, null);
+ }
+
+ public LuceneFunctionContext(LuceneQueryProvider provider, String indexName, CollectorManager<C> manager) {
+ this(provider, indexName, manager, LuceneQueryFactory.DEFAULT_LIMIT);
+ }
+
+ public LuceneFunctionContext(LuceneQueryProvider provider, String indexName, CollectorManager<C> manager, int limit) {
+ this.queryProvider = provider;
+ this.indexName = indexName;
+ this.manager = manager;
+ this.limit = limit;
+ }
+
+ /**
+ * @return The maximum count of result objects to be produced by the function
+ */
+ public int getLimit() {
+ return limit;
+ }
+
+ /**
+ * Get the name of the index to query
+ */
+ public String getIndexName() {
+ return indexName;
+ }
+
+ /**
+ * On each member, search query is executed on one or more {@link IndexRepository}s. A {@link CollectorManager} could
+ * be provided to customize the way results from these repositories is collected and merged.
+ *
+ * @return {@link CollectorManager} instance to be used by function
+ */
+ public CollectorManager<C> getCollectorManager() {
+ return this.manager;
+ }
+
+ public LuceneQueryProvider getQueryProvider() {
+ return queryProvider;
+ }
+
+ @Override
+ public int getDSFID() {
+ return LUCENE_FUNCTION_CONTEXT;
+ }
+
+ @Override
+ public void toData(DataOutput out) throws IOException {
+ out.writeInt(limit);
+ DataSerializer.writeObject(queryProvider, out);
+ DataSerializer.writeObject(manager, out);
+ DataSerializer.writeString(indexName, out);
+ }
+
+ @Override
+ public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+ limit = in.readInt();
+ queryProvider = DataSerializer.readObject(in);
+ manager = DataSerializer.readObject(in);
+ this.indexName = DataSerializer.readString(in);
+ }
+
+ @Override
+ public Version[] getSerializationVersions() {
+ return null;
+ }
+}