http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/main/java/mvm/rya/shell/util/JLinePrompt.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/main/java/mvm/rya/shell/util/JLinePrompt.java b/extras/rya.console/src/main/java/mvm/rya/shell/util/JLinePrompt.java new file mode 100644 index 0000000..5af4278 --- /dev/null +++ b/extras/rya.console/src/main/java/mvm/rya/shell/util/JLinePrompt.java @@ -0,0 +1,200 @@ +/** + * 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 mvm.rya.shell.util; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.util.Set; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.util.FieldUtils; +import org.springframework.shell.core.Shell; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; + +import jline.console.ConsoleReader; + +/** + * Provides access to the host {@link Shell}'s {@link ConsoleReader} and some + * utility functions for using it. + */ +@ParametersAreNonnullByDefault +public abstract class JLinePrompt { + + /** + * Defines things that may be typed in response to a boolean prompt that evaluate to true. + */ + private static final Set<String> affirmativeStrings = Sets.newHashSet("true", "t", "yes", "y"); + + /** + * Defines things that may be typed in response to a boolean prompt that evaluate to false. + */ + private static final Set<String> negativeStrings = Sets.newHashSet("false", "f", "no", "n"); + + @Autowired + private Shell shell; + + /** + * @return The shell's {@link ConsoleReader}. + */ + public ConsoleReader getReader() { + // XXX Spring Shell doesn't expose the reader that we need to use to + // read values from a terminal, so use reflection to pull it out. + return (ConsoleReader) FieldUtils.getProtectedFieldValue("reader", shell); + } + + /** + * Formats a prompt that shows a default value. + * + * @param fieldName - The text portion that appears before the default. (not null) + * @param defaultValue - The default value that will be shown in the prompt. + * @return A prompt that shows the default value for a field. + */ + public String makeFieldPrompt(final String fieldName, final boolean defaultValue) { + return String.format("%s [default: %s]: ", fieldName, defaultValue); + } + + /** + * Checks if a user's input matches one of the affirmative strings. + * + * @param input - The user's input. (not null) + * @return {@code true} if the input is one of the affirmative values; otherwise {@code false}. + */ + private boolean isAffirmative(final String input) { + requireNonNull(input); + for(final String affirmativeString : affirmativeStrings) { + if( input.equalsIgnoreCase(affirmativeString) ) { + return true; + } + } + return false; + } + + /** + * Checks if a user's input matches one of the negative strings. + * + * @param input - The user's input. (not null) + * @return {@code true} if the input is one of the negative values; otherwise {@code false}. + */ + private boolean isNegative(final String input) { + requireNonNull(input); + for(final String negativeString : negativeStrings) { + if( input.equalsIgnoreCase(negativeString) ) { + return true; + } + } + return false; + } + + /** + * Prompts a user for a boolean value. The prompt will be repeated until + * a value true/false string has been submitted. If a default value is + * provided, then it will be used if the user doens't enter anything. + * + * @param prompt - The prompt for the input. (not null) + * @param defaultValue - The default value for the input if one is provided. (not null) + * @return The value the user entered. + * @throws IOException There was a problem reading values from the user. + */ + protected boolean promptBoolean(final String prompt, final Optional<Boolean> defaultValue) throws IOException { + requireNonNull(prompt); + requireNonNull(defaultValue); + + final ConsoleReader reader = getReader(); + reader.setPrompt(prompt); + + Boolean value = null; + boolean prompting = true; + + while(prompting) { + // An empty input means to use the default value. + final String input = reader.readLine(); + if(input.isEmpty() && defaultValue.isPresent()) { + value = defaultValue.get(); + prompting = false; + } + + // Check if it is one of the affirmative answers. + if(isAffirmative(input)) { + value = true; + prompting = false; + } + + // Check if it is one of the negative answers. + if(isNegative(input)) { + value = false; + prompting = false; + } + + // If we are still prompting, the input was invalid. + if(prompting) { + reader.println("Invalid response (true/false)"); + } + } + + return value; + } + + /** + * Prompts a user for a String value. The prompt will be repeated until a + * value has been submitted. If a default value is provided, then it will be + * used if the user doesn't enter anything. + * + * @param prompt - The prompt for the input. (not null) + * @param defaultValue - The default value for the input if one is provided. (not null) + * @return The value the user entered. + * @throws IOException There was a problem reading values from the user. + */ + protected String promptString(final String prompt, final Optional<String> defaultValue) throws IOException { + requireNonNull(prompt); + requireNonNull(defaultValue); + + final ConsoleReader reader = getReader(); + reader.setPrompt(prompt); + + String value = null; + boolean prompting = true; + + while(prompting) { + // Read a line of input. + final String input = reader.readLine(); + + if(!input.isEmpty()) { + // If a value was provided, return it. + value = input; + prompting = false; + } else { + // Otherwise, if a default value was provided, return it; + if(defaultValue.isPresent()) { + value = defaultValue.get(); + prompting = false; + } else { + // Otherwise, the user must provide a value. + reader.println("Invalid response. Must provide a value."); + } + } + } + + return value; + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/main/java/mvm/rya/shell/util/PasswordPrompt.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/main/java/mvm/rya/shell/util/PasswordPrompt.java b/extras/rya.console/src/main/java/mvm/rya/shell/util/PasswordPrompt.java new file mode 100644 index 0000000..c4a7d90 --- /dev/null +++ b/extras/rya.console/src/main/java/mvm/rya/shell/util/PasswordPrompt.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 mvm.rya.shell.util; + +import java.io.IOException; + +import javax.annotation.ParametersAreNonnullByDefault; + +import jline.console.ConsoleReader; + +/** + * A mechanism for prompting a user of the application for a password. + */ +@ParametersAreNonnullByDefault +public interface PasswordPrompt { + + /** + * Prompt the user for a password, wait for their input, and then get the + * value they entered. + * + * @return A character array holding the entered password. + * @throws IOEXception There was a problem reading the password. + */ + public char[] getPassword() throws IOException; + + /** + * Prompts a user for their password using a JLine {@link ConsoleReader}. + * <p> + * This prompt has a known security issue. ConsoleReader only reads passwords + * into Strings, so they can't easily be cleared out. We many an attempt to + * garbage collect the String after converting it to a character array, but + * this could be improved. + */ + public static class JLinePasswordPrompt extends JLinePrompt implements PasswordPrompt { + + @Override + public char[] getPassword() throws IOException { + char[] password = new char[0]; + + final ConsoleReader reader = getReader(); + reader.setPrompt("Password: "); + String passwordStr = reader.readLine('*'); + password = passwordStr.toCharArray(); + + // Reading the password into memory as a String is less safe than a char[] + // because the String is immutable. We can't clear it out. At best, we can + // remove all references to it and suggest the GC clean it up. There are no + // guarantees though. + passwordStr = null; + System.gc(); + + return password; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/main/java/mvm/rya/shell/util/RyaDetailsFormatter.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/main/java/mvm/rya/shell/util/RyaDetailsFormatter.java b/extras/rya.console/src/main/java/mvm/rya/shell/util/RyaDetailsFormatter.java new file mode 100644 index 0000000..4c83b8d --- /dev/null +++ b/extras/rya.console/src/main/java/mvm/rya/shell/util/RyaDetailsFormatter.java @@ -0,0 +1,117 @@ +/** + * 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 mvm.rya.shell.util; + +import static java.util.Objects.requireNonNull; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails; + +/** + * Formats an instance of {@link RyaDetails}. + */ +@ParametersAreNonnullByDefault +public class RyaDetailsFormatter { + + /** + * Pretty formats an instance of {@link RyaDetails}. + * + * @param details - The object to format. (not null) + * @return A pretty render of the object. + */ + public String format(final RyaDetails details) { + requireNonNull(details); + + final StringBuilder report = new StringBuilder(); + + report.append("General Metadata:\n"); + report.append(" Instance Name: ").append(details.getRyaInstanceName()).append("\n"); + report.append(" RYA Version: ").append( details.getRyaVersion() ).append("\n"); + + report.append("Secondary Indicies:\n"); + report.append(" Entity Centric Index:\n"); + report.append(" Enabled: ").append( details.getEntityCentricIndexDetails().isEnabled() ).append("\n"); + report.append(" Geospatial Index:\n"); + report.append(" Enabled: ").append( details.getGeoIndexDetails().isEnabled() ).append("\n"); + report.append(" Free Text Index:\n"); + report.append(" Enabled: ").append( details.getFreeTextIndexDetails().isEnabled() ).append("\n"); + report.append(" Temporal Index:\n"); + report.append(" Enabled: ").append( details.getTemporalIndexDetails().isEnabled() ).append("\n"); + + report.append(" PCJ Index:\n"); + final PCJIndexDetails pcjDetails = details.getPCJIndexDetails(); + report.append(" Enabled: ").append( pcjDetails.isEnabled() ).append("\n"); + if(pcjDetails.isEnabled()) { + if(pcjDetails.getFluoDetails().isPresent()) { + final String fluoAppName = pcjDetails.getFluoDetails().get().getUpdateAppName(); + report.append(" Fluo App Name: ").append(fluoAppName).append("\n"); + } + + final ImmutableMap<String, PCJDetails> pcjs = pcjDetails.getPCJDetails(); + report.append(" PCJs:\n"); + if(pcjs.isEmpty()) { + report.append(" No PCJs have been added yet.\n"); + } else { + for(final PCJDetails pcj : pcjs.values()) { + report.append(" ID: ").append(pcj.getId()).append("\n"); + + final String updateStrategy = format( pcj.getUpdateStrategy(), "None" ); + report.append(" Update Strategy: ").append(updateStrategy).append("\n"); + + final String lastUpdateTime = format( pcj.getLastUpdateTime(), "unavailable"); + report.append(" Last Update Time: ").append(lastUpdateTime).append("\n"); + } + } + } + + report.append("Statistics:\n"); + report.append(" Prospector:\n"); + final String prospectorLastUpdateTime = format(details.getProspectorDetails().getLastUpdated(), "unavailable"); + report.append(" Last Update Time: ").append( prospectorLastUpdateTime).append("\n"); + + report.append(" Join Selectivity:\n"); + final String jsLastUpdateTime = format(details.getJoinSelectivityDetails().getLastUpdated(), "unavailable"); + report.append(" Last Updated Time: ").append( jsLastUpdateTime ).append("\n"); + + return report.toString(); + } + + /** + * Formats an optional value using the value's toString() value. + * + * @param value - The optional value that will be formatted. (not null) + * @param absentValue - The String that will be returned if the optional is absent. (not null) + * @return The String representation of the optional value. + */ + private <T> String format(final Optional<T> value, final String absentValue) { + requireNonNull(value); + + String formatted = "unavailable"; + if(value.isPresent()) { + formatted = value.get().toString(); + } + return formatted; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/main/java/mvm/rya/shell/util/SparqlPrompt.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/main/java/mvm/rya/shell/util/SparqlPrompt.java b/extras/rya.console/src/main/java/mvm/rya/shell/util/SparqlPrompt.java new file mode 100644 index 0000000..e47e292 --- /dev/null +++ b/extras/rya.console/src/main/java/mvm/rya/shell/util/SparqlPrompt.java @@ -0,0 +1,55 @@ +/** + * 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 mvm.rya.shell.util; + +import java.io.IOException; + +import javax.annotation.ParametersAreNonnullByDefault; + +import jline.console.ConsoleReader; + +/** + * A mechanism for prompting a user of the application for a SPARQL string. + */ +@ParametersAreNonnullByDefault +public interface SparqlPrompt { + + /** + * Prompt the user for a SPARQL query, wait for their input, and then get the + * value they entered. + * + * @return The user entered SPARQL query. + * @throws IOEXception There was a problem reading the user's input. + */ + public String getSparql() throws IOException; + + /** + * Prompts a user for a SPARQL query using a JLine {@link ConsoleReader}. + */ + @ParametersAreNonnullByDefault + public static class JLineSparqlPrompt extends JLinePrompt implements SparqlPrompt { + + @Override + public String getSparql() throws IOException { + final ConsoleReader reader = getReader(); + reader.setPrompt("SPARQL: "); + return reader.readLine(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/main/resources/LICENSE.txt ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/main/resources/LICENSE.txt b/extras/rya.console/src/main/resources/LICENSE.txt new file mode 100644 index 0000000..4a9fe83 --- /dev/null +++ b/extras/rya.console/src/main/resources/LICENSE.txt @@ -0,0 +1,16 @@ +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. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/main/resources/META-INF/spring/spring-shell-plugin.xml ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/main/resources/META-INF/spring/spring-shell-plugin.xml b/extras/rya.console/src/main/resources/META-INF/spring/spring-shell-plugin.xml index e593a48..b210311 100644 --- a/extras/rya.console/src/main/resources/META-INF/spring/spring-shell-plugin.xml +++ b/extras/rya.console/src/main/resources/META-INF/spring/spring-shell-plugin.xml @@ -1,30 +1,40 @@ <?xml version="1.0" encoding="UTF-8"?> - <!-- -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 + 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 -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. ---> + 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. + +--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> - <context:component-scan base-package="mvm.rya.console" /> + <!-- Tell Spring where it can find all of the Command components. --> + <context:component-scan base-package="mvm.rya.shell"/> -</beans> + <!-- Define the shell state bean that will be shared across all of the commands. --> + <bean id="sharedShellState" class="mvm.rya.shell.SharedShellState" /> + <bean id="passwordPrompt" class="mvm.rya.shell.util.PasswordPrompt.JLinePasswordPrompt" /> + <bean id="installPrompt" class="mvm.rya.shell.util.InstallPrompt.JLineAccumuloInstallPrompt" /> + <bean id="sparqlPrompt" class="mvm.rya.shell.util.SparqlPrompt.JLineSparqlPrompt" /> + + <!-- Define each of the beans that hold onto commands used by the shell. --> + <bean id="ryaConnectionCommands" class="mvm.rya.shell.RyaConnectionCommands" /> + <bean id="ryaAdminCommands" class="mvm.rya.shell.RyaAdminCommands" /> +</beans> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/RyaAdminCommandsTest.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/RyaAdminCommandsTest.java b/extras/rya.console/src/test/java/mvm/rya/shell/RyaAdminCommandsTest.java new file mode 100644 index 0000000..1baec41 --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/RyaAdminCommandsTest.java @@ -0,0 +1,261 @@ +/** + * 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 mvm.rya.shell; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.Date; +import java.util.List; + +import org.junit.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; + +import mvm.rya.api.client.RyaClientException; +import mvm.rya.api.client.RyaClient; +import mvm.rya.api.client.CreatePCJ; +import mvm.rya.api.client.DeletePCJ; +import mvm.rya.api.client.GetInstanceDetails; +import mvm.rya.api.client.Install; +import mvm.rya.api.client.InstanceDoesNotExistException; +import mvm.rya.api.client.ListInstances; +import mvm.rya.api.client.Install.DuplicateInstanceNameException; +import mvm.rya.api.client.Install.InstallConfiguration; +import mvm.rya.api.client.accumulo.AccumuloConnectionDetails; +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetails.EntityCentricIndexDetails; +import mvm.rya.api.instance.RyaDetails.FreeTextIndexDetails; +import mvm.rya.api.instance.RyaDetails.GeoIndexDetails; +import mvm.rya.api.instance.RyaDetails.JoinSelectivityDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.FluoDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails.PCJUpdateStrategy; +import mvm.rya.api.instance.RyaDetails.ProspectorDetails; +import mvm.rya.api.instance.RyaDetails.TemporalIndexDetails; +import mvm.rya.shell.util.InstallPrompt; +import mvm.rya.shell.util.SparqlPrompt; + +/** + * Unit tests the methods of {@link RyaAdminCommands}. + */ +public class RyaAdminCommandsTest { + + @Test + public void createPCJ() throws InstanceDoesNotExistException, RyaClientException, IOException { + // Mock the object that performs the create operation. + final String instanceName = "unitTest"; + final String sparql = "SELECT * WHERE { ?person <http://isA> ?noun }"; + final String pcjId = "123412342"; + final CreatePCJ mockCreatePCJ = mock(CreatePCJ.class); + when(mockCreatePCJ.createPCJ( eq(instanceName), eq(sparql) ) ).thenReturn( pcjId ); + + final RyaClient mockCommands = mock(RyaClient.class); + when(mockCommands.getCreatePCJ()).thenReturn( mockCreatePCJ ); + + final SharedShellState state = new SharedShellState(); + state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockCommands); + state.connectedToInstance(instanceName); + + final SparqlPrompt mockSparqlPrompt = mock(SparqlPrompt.class); + when(mockSparqlPrompt.getSparql()).thenReturn(sparql); + + // Execute the command. + final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mockSparqlPrompt); + final String message = commands.createPcj(); + + // Verify the values that were provided to the command were passed through to CreatePCJ. + verify(mockCreatePCJ).createPCJ(eq(instanceName), eq(sparql)); + + // Verify a message is returned that explains what was created. + final String expected = "The PCJ has been created. Its ID is '123412342'."; + assertEquals(expected, message); + } + + @Test + public void deletePCJ() throws InstanceDoesNotExistException, RyaClientException { + // Mock the object that performs the delete operation. + final DeletePCJ mockDeletePCJ = mock(DeletePCJ.class); + + final RyaClient mockCommands = mock(RyaClient.class); + when(mockCommands.getDeletePCJ()).thenReturn( mockDeletePCJ ); + + final SharedShellState state = new SharedShellState(); + state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockCommands); + final String instanceName = "unitTests"; + state.connectedToInstance(instanceName); + + // Execute the command. + final String pcjId = "123412342"; + + final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class)); + final String message = commands.deletePcj(pcjId); + + // Verify the values that were provided to the command were passed through to the DeletePCJ. + verify(mockDeletePCJ).deletePCJ(eq(instanceName), eq(pcjId)); + + // Verify a message is returned that explains what was deleted. + final String expected = "The PCJ has been deleted."; + assertEquals(expected, message); + } + + @Test + public void getInstanceDetails() throws InstanceDoesNotExistException, RyaClientException { + // Mock the object that performs the get operation. + final GetInstanceDetails mockGetInstanceDetails = mock(GetInstanceDetails.class); + final String instanceName = "test_instance"; + final RyaDetails details = RyaDetails.builder().setRyaInstanceName(instanceName) + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date(1252521351L) )) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL))) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date(12525211L))) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date(125221351L))) ) + .build(); + + when(mockGetInstanceDetails.getDetails(eq(instanceName))).thenReturn( Optional.of(details) ); + + final RyaClient mockCommands = mock(RyaClient.class); + when(mockCommands.getGetInstanceDetails()).thenReturn( mockGetInstanceDetails ); + + final SharedShellState state = new SharedShellState(); + state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockCommands); + state.connectedToInstance(instanceName); + + // Execute the command. + final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class)); + final String message = commands.getInstanceDetails(); + + // Verify the values that were provided to the command were passed through to the GetInstanceDetails. + verify(mockGetInstanceDetails).getDetails(eq(instanceName)); + + // Verify a message is returned that includes the details. + final String expected = + "General Metadata:\n" + + " Instance Name: test_instance\n" + + " RYA Version: 1.2.3.4\n" + + "Secondary Indicies:\n" + + " Entity Centric Index:\n" + + " Enabled: true\n" + + " Geospatial Index:\n" + + " Enabled: true\n" + + " Free Text Index:\n" + + " Enabled: true\n" + + " Temporal Index:\n" + + " Enabled: true\n" + + " PCJ Index:\n" + + " Enabled: true\n" + + " Fluo App Name: test_instance_rya_pcj_updater\n" + + " PCJs:\n" + + " ID: pcj 1\n" + + " Update Strategy: BATCH\n" + + " Last Update Time: Thu Jan 15 06:55:21 EST 1970\n" + + " ID: pcj 2\n" + + " Update Strategy: INCREMENTAL\n" + + " Last Update Time: unavailable\n" + + "Statistics:\n" + + " Prospector:\n" + + " Last Update Time: Wed Dec 31 22:28:45 EST 1969\n" + + " Join Selectivity:\n" + + " Last Updated Time: Fri Jan 02 05:47:01 EST 1970\n"; + assertEquals(expected, message); + } + + @Test + public void install() throws DuplicateInstanceNameException, RyaClientException, IOException { + // Mock the object that performs the install operation. + final Install mockInstall = mock(Install.class); + + final RyaClient mockCommands = mock(RyaClient.class); + when(mockCommands.getInstall()).thenReturn( mockInstall ); + + final SharedShellState state = new SharedShellState(); + state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockCommands); + + // Execute the command. + final String instanceName = "unitTests"; + final InstallConfiguration installConfig = InstallConfiguration.builder() + .setEnableGeoIndex(true) + .setEnablePcjIndex(true) + .build(); + + final InstallPrompt mockInstallPrompt = mock(InstallPrompt.class); + when(mockInstallPrompt.promptInstanceName()).thenReturn( instanceName ); + when(mockInstallPrompt.promptInstallConfiguration()).thenReturn( installConfig ); + when(mockInstallPrompt.promptVerified(eq(instanceName), eq(installConfig))).thenReturn(true); + + final RyaAdminCommands commands = new RyaAdminCommands(state, mockInstallPrompt, mock(SparqlPrompt.class)); + final String message = commands.install(); + + // Verify the values that were provided to the command were passed through to the Install. + verify(mockInstall).install(eq(instanceName), eq(installConfig)); + + // Verify a message is returned that indicates the success of the operation. + final String expected = "The Rya instance named 'unitTests' has been installed."; + assertEquals(expected, message); + } + + @Test + public void listInstances() throws RyaClientException, IOException { + // Mock the object that performs the list operation. + final ListInstances mockListInstances = mock(ListInstances.class); + final List<String> instanceNames = Lists.newArrayList("a", "b", "c", "d"); + when(mockListInstances.listInstances()).thenReturn(instanceNames); + + final RyaClient mockCommands = mock(RyaClient.class); + when(mockCommands.getListInstances()).thenReturn( mockListInstances ); + + final SharedShellState state = new SharedShellState(); + state.connectedToAccumulo(mock(AccumuloConnectionDetails.class), mockCommands); + state.connectedToInstance("b"); + + // Execute the command. + final RyaAdminCommands commands = new RyaAdminCommands(state, mock(InstallPrompt.class), mock(SparqlPrompt.class)); + final String message = commands.listInstances(); + + // Verify a message is returned that lists the the instances. + final String expected = + "Rya instance names:\n" + + " a\n" + + " * b\n" + + " c\n" + + " d\n"; + assertEquals(expected, message); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/RyaConnectionCommandsIT.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/RyaConnectionCommandsIT.java b/extras/rya.console/src/test/java/mvm/rya/shell/RyaConnectionCommandsIT.java new file mode 100644 index 0000000..c755181 --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/RyaConnectionCommandsIT.java @@ -0,0 +1,269 @@ +/** + * 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. + */ +///** +// * 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 mvm.rya.shell; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.apache.accumulo.minicluster.MiniAccumuloCluster; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandResult; +import org.springframework.shell.core.JLineShellComponent; + +import mvm.rya.api.client.Install.InstallConfiguration; +import mvm.rya.shell.SharedShellState.ConnectionState; +import mvm.rya.shell.SharedShellState.ShellState; +import mvm.rya.shell.util.InstallPrompt; +import mvm.rya.shell.util.PasswordPrompt; + +/** + * Integration tests the methods of {@link RyaConnectionCommands}. + */ +public class RyaConnectionCommandsIT extends RyaShellITBase { + + @Test + public void connectAccumulo() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the correct password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("password".toCharArray()); + + // Execute the connect command. + final String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + + final CommandResult connectResult = shell.executeCommand(cmd); + + // Ensure the connection was successful. + assertTrue( connectResult.isSuccess() ); + } + + @Test + public void connectAccumulo_noAuths() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the correct password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("password".toCharArray()); + + // Execute the command + final String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + + final CommandResult connectResult = shell.executeCommand(cmd); + + // Ensure the connection was successful. + assertTrue( connectResult.isSuccess() ); + } + + @Test + public void connectAccumulo_wrongCredentials() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the wrong password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("asjifo[ijwa".toCharArray()); + + // Execute the command + final String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + + final CommandResult connectResult = shell.executeCommand(cmd); + + // Ensure the command failed. + assertFalse( connectResult.isSuccess() ); + } + + @Test + public void printConnectionDetails_notConnected() { + final JLineShellComponent shell = getTestShell(); + + // Run the print connection details command. + final CommandResult printResult = shell.executeCommand( RyaConnectionCommands.PRINT_CONNECTION_DETAILS_CMD ); + final String msg = (String) printResult.getResult(); + + final String expected = "The shell is not connected to anything."; + assertEquals(expected, msg); + } + + @Test + public void printConnectionDetails_connectedToAccumulo() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the correct password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("password".toCharArray()); + + // Connect to the mini accumulo instance. + final String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + shell.executeCommand(cmd); + + // Run the print connection details command. + final CommandResult printResult = shell.executeCommand( RyaConnectionCommands.PRINT_CONNECTION_DETAILS_CMD ); + final String msg = (String) printResult.getResult(); + + final String expected = + "The shell is connected to an instance of Accumulo using the following parameters:\n" + + " Username: root\n" + + " Instance Name: " + cluster.getInstanceName() + "\n" + + " Zookeepers: " + cluster.getZooKeepers(); + assertEquals(expected, msg); + } + + @Test + public void connectToInstance() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the correct password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("password".toCharArray()); + + // Connect to the mini accumulo instance. + String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + CommandResult result = shell.executeCommand(cmd); + + // Install an instance of rya. + final String instanceName = "testInstance"; + final InstallConfiguration installConf = InstallConfiguration.builder().build(); + + final InstallPrompt installPrompt = context.getBean( InstallPrompt.class ); + when(installPrompt.promptInstanceName()).thenReturn("testInstance"); + when(installPrompt.promptInstallConfiguration()).thenReturn( installConf ); + when(installPrompt.promptVerified(instanceName, installConf)).thenReturn(true); + + result = shell.executeCommand( RyaAdminCommands.INSTALL_CMD ); + assertTrue( result.isSuccess() ); + + // Connect to the instance that was just installed. + cmd = RyaConnectionCommands.CONNECT_INSTANCE_CMD + " --instance " + instanceName; + result = shell.executeCommand(cmd); + assertTrue( result.isSuccess() ); + + // Verify the shell state indicates it is connected to an instance. + final SharedShellState sharedState = context.getBean( SharedShellState.class ); + final ShellState state = sharedState.getShellState(); + assertEquals(ConnectionState.CONNECTED_TO_INSTANCE, state.getConnectionState()); + } + + @Test + public void connectToInstance_instanceDoesNotExist() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the correct password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("password".toCharArray()); + + // Connect to the mini accumulo instance. + String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + shell.executeCommand(cmd); + + // Try to connect to a non-existing instance. + cmd = RyaConnectionCommands.CONNECT_INSTANCE_CMD + " --instance doesNotExist"; + final CommandResult result = shell.executeCommand(cmd); + assertFalse( result.isSuccess() ); + } + + @Test + public void disconnect() throws IOException { + final MiniAccumuloCluster cluster = getCluster(); + final Bootstrap bootstrap = getTestBootstrap(); + final JLineShellComponent shell = getTestShell(); + + // Mock the user entering the correct password. + final ApplicationContext context = bootstrap.getApplicationContext(); + final PasswordPrompt mockPrompt = context.getBean( PasswordPrompt.class ); + when(mockPrompt.getPassword()).thenReturn("password".toCharArray()); + + // Connect to the mini accumulo instance. + final String cmd = + RyaConnectionCommands.CONNECT_ACCUMULO_CMD + " " + + "--username root " + + "--instanceName " + cluster.getInstanceName() + " "+ + "--zookeepers " + cluster.getZooKeepers(); + shell.executeCommand(cmd); + + // Disconnect from it. + final CommandResult disconnectResult = shell.executeCommand( RyaConnectionCommands.DISCONNECT_COMMAND_NAME_CMD ); + assertTrue( disconnectResult.isSuccess() ); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/RyaPromptProviderTest.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/RyaPromptProviderTest.java b/extras/rya.console/src/test/java/mvm/rya/shell/RyaPromptProviderTest.java new file mode 100644 index 0000000..54a0bba --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/RyaPromptProviderTest.java @@ -0,0 +1,80 @@ +/** + * 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 mvm.rya.shell; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import mvm.rya.api.client.RyaClient; +import mvm.rya.api.client.accumulo.AccumuloConnectionDetails; + +/** + * Tests the methods of {@link RyaPromptProvider}. + */ +public class RyaPromptProviderTest { + + @Test + public void notConnected() { + // Create a shared state that is disconnected. + final SharedShellState sharedState = new SharedShellState(); + sharedState.disconnected(); + + // Create the prompt. + final String prompt = new RyaPromptProvider(sharedState).getPrompt(); + + // Verify the prompt is formatted correctly. + final String expected = "rya> "; + assertEquals(expected, prompt); + } + + @Test + public void isConnected_noInstanceName() { + // Create a shared state that is connected to a storage, but not a rya instance. + final SharedShellState sharedState = new SharedShellState(); + + final AccumuloConnectionDetails connectionDetails = new AccumuloConnectionDetails("", new char[]{}, "testInstance", ""); + sharedState.connectedToAccumulo(connectionDetails, mock(RyaClient.class)); + + // Create a prompt. + final String prompt = new RyaPromptProvider(sharedState).getPrompt(); + + // Verify the prompt is formatted correctly. + final String expected = "rya/testInstance> "; + assertEquals(expected, prompt); + } + + @Test + public void isConnected_hasInstanceName() { + // Create a shared state that is connected to a specific instance. + final SharedShellState sharedState = new SharedShellState(); + + final AccumuloConnectionDetails connectionDetails = new AccumuloConnectionDetails("", new char[]{}, "testInstance", ""); + sharedState.connectedToAccumulo(connectionDetails, mock(RyaClient.class)); + sharedState.connectedToInstance("testRya"); + + // Create a prompt. + final String prompt = new RyaPromptProvider(sharedState).getPrompt(); + + // Verify the prompt is formatted correctly. + final String expected = "rya/testInstance:testRya> "; + assertEquals(expected, prompt); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/RyaShellITBase.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/RyaShellITBase.java b/extras/rya.console/src/test/java/mvm/rya/shell/RyaShellITBase.java new file mode 100644 index 0000000..162cecf --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/RyaShellITBase.java @@ -0,0 +1,103 @@ +/** + * 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 mvm.rya.shell; + +import java.io.IOException; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.apache.accumulo.minicluster.MiniAccumuloCluster; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.zookeeper.ClientCnxn; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.JLineShellComponent; + +import mvm.rya.accumulo.MiniAccumuloClusterInstance; + +/** + * All Rya Shell integration tests should extend this one. It provides startup + * and shutdown hooks for a Mini Accumulo Cluster when you start and stop testing. + * It also creates a new shell to test with between each test. + */ +public class RyaShellITBase { + + /** + * The cluster that will be used. + */ + private MiniAccumuloClusterInstance cluster = null; + + /** + * The bootstrap that was used to initialize the Shell that will be tested. + */ + private Bootstrap bootstrap; + + /** + * The shell that will be tested. + */ + private JLineShellComponent shell; + + @BeforeClass + public static void killLoudLogs() { + Logger.getLogger(ClientCnxn.class).setLevel(Level.ERROR); + } + + @Before + public void startShell() throws IOException, InterruptedException, AccumuloException, AccumuloSecurityException { + // Start the cluster. + cluster = new MiniAccumuloClusterInstance(); + cluster.startMiniAccumulo(); + + // Bootstrap the shell with the test bean configuration. + bootstrap = new Bootstrap(new String[]{}, new String[]{"file:src/test/resources/RyaShellTest-context.xml"}); + shell = bootstrap.getJLineShellComponent(); + } + + @After + public void stopShell() throws IOException, InterruptedException { + shell.stop(); + + // Stop the cluster. + cluster.stopMiniAccumulo(); + } + + /** + * @return The bootstrap that was used to initialize the Shell that will be tested. + */ + public Bootstrap getTestBootstrap() { + return bootstrap; + } + + /** + * @return The shell that will be tested. + */ + public JLineShellComponent getTestShell() { + return shell; + } + + /** + * @return The cluster that is hosting the test. + */ + public MiniAccumuloCluster getCluster() { + return cluster.getCluster(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/SharedShellStateTest.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/SharedShellStateTest.java b/extras/rya.console/src/test/java/mvm/rya/shell/SharedShellStateTest.java new file mode 100644 index 0000000..e5f0341 --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/SharedShellStateTest.java @@ -0,0 +1,167 @@ +/** + * 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 mvm.rya.shell; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import mvm.rya.api.client.RyaClient; +import mvm.rya.api.client.accumulo.AccumuloConnectionDetails; +import mvm.rya.shell.SharedShellState.ConnectionState; +import mvm.rya.shell.SharedShellState.ShellState; + +/** + * Tests the methods of {@link SharedShellState}. + */ +public class SharedShellStateTest { + + @Test + public void initialStateIsDisconnected() { + final SharedShellState state = new SharedShellState(); + + // Verify disconnected and no values are set. + final ShellState expected = ShellState.builder() + .setConnectionState(ConnectionState.DISCONNECTED) + .build(); + + assertEquals(expected, state.getShellState()); + } + + @Test + public void disconnectedToConnectedToStorage() { + final SharedShellState state = new SharedShellState(); + + // Connect to Accumulo. + final AccumuloConnectionDetails connectionDetails = mock(AccumuloConnectionDetails.class); + final RyaClient connectedCommands = mock(RyaClient.class); + state.connectedToAccumulo(connectionDetails, connectedCommands); + + // Verify the state. + final ShellState expected = ShellState.builder() + .setConnectionState(ConnectionState.CONNECTED_TO_STORAGE) + .setAccumuloConnectionDetails(connectionDetails) + .setConnectedCommands(connectedCommands) + .build(); + + assertEquals(expected, state.getShellState()); + } + + @Test(expected = IllegalStateException.class) + public void connectToStorageAgain() { + final SharedShellState state = new SharedShellState(); + + // Connect to Accumulo. + final AccumuloConnectionDetails connectionDetails = mock(AccumuloConnectionDetails.class); + final RyaClient connectedCommands = mock(RyaClient.class); + state.connectedToAccumulo(connectionDetails, connectedCommands); + + // Try to set the information again. + state.connectedToAccumulo(connectionDetails, connectedCommands); + } + + @Test + public void connectedToInstance() { + final SharedShellState state = new SharedShellState(); + + // Connect to Accumulo. + final AccumuloConnectionDetails connectionDetails = mock(AccumuloConnectionDetails.class); + final RyaClient connectedCommands = mock(RyaClient.class); + state.connectedToAccumulo(connectionDetails, connectedCommands); + + // Connect to an Instance. + state.connectedToInstance("instance"); + + // Verify the state. + final ShellState expected = ShellState.builder() + .setConnectionState(ConnectionState.CONNECTED_TO_INSTANCE) + .setAccumuloConnectionDetails(connectionDetails) + .setConnectedCommands(connectedCommands) + .setRyaInstanceName("instance") + .build(); + + assertEquals(expected, state.getShellState()); + } + + @Test + public void ConnectedToInstanceAgain() { + final SharedShellState state = new SharedShellState(); + + // Connect to Accumulo. + final AccumuloConnectionDetails connectionDetails = mock(AccumuloConnectionDetails.class); + final RyaClient connectedCommands = mock(RyaClient.class); + state.connectedToAccumulo(connectionDetails, connectedCommands); + + // Connect to an Instance. + state.connectedToInstance("instance"); + + // Connect to another instance. + state.connectedToInstance("secondInstance"); + + // Verify the state. + final ShellState expected = ShellState.builder() + .setConnectionState(ConnectionState.CONNECTED_TO_INSTANCE) + .setAccumuloConnectionDetails(connectionDetails) + .setConnectedCommands(connectedCommands) + .setRyaInstanceName("secondInstance") + .build(); + assertEquals(expected, state.getShellState()); + } + + @Test(expected = IllegalStateException.class) + public void connectedToInstanceWhileDisconnectedFromStorage() { + final SharedShellState state = new SharedShellState(); + + state.connectedToInstance("instance"); + } + + @Test + public void disconnected() { + final SharedShellState state = new SharedShellState(); + + // Connect to Accumulo and an instance. + final AccumuloConnectionDetails connectionDetails = mock(AccumuloConnectionDetails.class); + final RyaClient connectedCommands = mock(RyaClient.class); + state.connectedToAccumulo(connectionDetails, connectedCommands); + state.connectedToInstance("instance"); + + // Disconnect. + state.disconnected(); + + // Verify the state. + final ShellState expected = ShellState.builder() + .setConnectionState(ConnectionState.DISCONNECTED) + .build(); + assertEquals(expected, state.getShellState()); + } + + @Test + public void disconnectedAgain() { + // Indicate we have diconnected while already in the disconnected state. + final SharedShellState state = new SharedShellState(); + state.disconnected(); + + // Verify the state. + final ShellState expected = ShellState.builder() + .setConnectionState(ConnectionState.DISCONNECTED) + .build(); + assertEquals(expected, state.getShellState()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/util/ConnectorFactoryIT.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/util/ConnectorFactoryIT.java b/extras/rya.console/src/test/java/mvm/rya/shell/util/ConnectorFactoryIT.java new file mode 100644 index 0000000..e96b0ad --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/util/ConnectorFactoryIT.java @@ -0,0 +1,57 @@ +/** + * 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 mvm.rya.shell.util; + +import java.nio.CharBuffer; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.junit.Test; + +import mvm.rya.accumulo.AccumuloITBase; + +/** + * Tests the methods of {@link ConnectorFactory}. + */ +public class ConnectorFactoryIT extends AccumuloITBase { + + @Test + public void connect_successful() throws AccumuloException, AccumuloSecurityException { + // Setup the values that will be tested with. + final CharSequence password = CharBuffer.wrap( getPassword() ); + + final ConnectorFactory ac = new ConnectorFactory(); + ac.connect(getUsername(), + password, + getInstanceName(), + getZookeepers()); + } + + @Test(expected = AccumuloSecurityException.class) + public void connect_wrongCredentials() throws AccumuloException, AccumuloSecurityException { + // Setup the values that will be tested with. + final CharSequence password = CharBuffer.wrap( new char[] {'w','r','o','n','g','p','a','s','s'} ); + + final ConnectorFactory ac = new ConnectorFactory(); + ac.connect(getUsername(), + password, + getInstanceName(), + getZookeepers()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/util/InstanceNamesFormatterTest.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/util/InstanceNamesFormatterTest.java b/extras/rya.console/src/test/java/mvm/rya/shell/util/InstanceNamesFormatterTest.java new file mode 100644 index 0000000..f10b776 --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/util/InstanceNamesFormatterTest.java @@ -0,0 +1,81 @@ +/** + * 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 mvm.rya.shell.util; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +import com.beust.jcommander.internal.Lists; + +/** + * Tests an instance of {@link InstanceNamesFormatter}. + */ +public class InstanceNamesFormatterTest { + + @Test + public void format_withConnectedName() { + final List<String> instanceNames = Lists.newArrayList("a", "b", "c", "d"); + + final String formatted = new InstanceNamesFormatter().format(instanceNames, "c"); + + final String expected = + "Rya instance names:\n" + + " a\n" + + " b\n" + + " * c\n" + + " d\n"; + + assertEquals(expected, formatted); + } + + @Test + public void format_connectedNameNotInList() { + final List<String> instanceNames = Lists.newArrayList("a", "b", "c", "d"); + + final String formatted = new InstanceNamesFormatter().format(instanceNames, "not_in_list"); + + final String expected = + "Rya instance names:\n" + + " a\n" + + " b\n" + + " c\n" + + " d\n"; + + assertEquals(expected, formatted); + } + + @Test + public void format() { + final List<String> instanceNames = Lists.newArrayList("a", "b", "c", "d"); + + final String formatted = new InstanceNamesFormatter().format(instanceNames); + + final String expected = + "Rya instance names:\n" + + " a\n" + + " b\n" + + " c\n" + + " d\n"; + + assertEquals(expected, formatted); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/java/mvm/rya/shell/util/RyaDetailsFormatterTest.java ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/java/mvm/rya/shell/util/RyaDetailsFormatterTest.java b/extras/rya.console/src/test/java/mvm/rya/shell/util/RyaDetailsFormatterTest.java new file mode 100644 index 0000000..d618283 --- /dev/null +++ b/extras/rya.console/src/test/java/mvm/rya/shell/util/RyaDetailsFormatterTest.java @@ -0,0 +1,106 @@ +/** + * 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 mvm.rya.shell.util; + +import static org.junit.Assert.assertEquals; + +import java.util.Date; + +import org.junit.Test; + +import com.google.common.base.Optional; + +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetails.EntityCentricIndexDetails; +import mvm.rya.api.instance.RyaDetails.FreeTextIndexDetails; +import mvm.rya.api.instance.RyaDetails.GeoIndexDetails; +import mvm.rya.api.instance.RyaDetails.JoinSelectivityDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.FluoDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails.PCJUpdateStrategy; +import mvm.rya.api.instance.RyaDetails.ProspectorDetails; +import mvm.rya.api.instance.RyaDetails.TemporalIndexDetails; + +/** + * Tests the methods of {@link RyaDetailsFormatter}. + */ +public class RyaDetailsFormatterTest { + + @Test + public void format() { + // Create the object that will be formatted. + final RyaDetails details = RyaDetails.builder().setRyaInstanceName("test_instance") + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date(1252521351L) )) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL))) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date(12525211L))) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date(125221351L))) ) + .build(); + + final String formatted = new RyaDetailsFormatter().format(details); + + // Verify the created object matches the expected result. + final String expected = + "General Metadata:\n" + + " Instance Name: test_instance\n" + + " RYA Version: 1.2.3.4\n" + + "Secondary Indicies:\n" + + " Entity Centric Index:\n" + + " Enabled: true\n" + + " Geospatial Index:\n" + + " Enabled: true\n" + + " Free Text Index:\n" + + " Enabled: true\n" + + " Temporal Index:\n" + + " Enabled: true\n" + + " PCJ Index:\n" + + " Enabled: true\n" + + " Fluo App Name: test_instance_rya_pcj_updater\n" + + " PCJs:\n" + + " ID: pcj 1\n" + + " Update Strategy: BATCH\n" + + " Last Update Time: Thu Jan 15 06:55:21 EST 1970\n" + + " ID: pcj 2\n" + + " Update Strategy: INCREMENTAL\n" + + " Last Update Time: unavailable\n" + + "Statistics:\n" + + " Prospector:\n" + + " Last Update Time: Wed Dec 31 22:28:45 EST 1969\n" + + " Join Selectivity:\n" + + " Last Updated Time: Fri Jan 02 05:47:01 EST 1970\n"; + + assertEquals(expected, formatted); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.console/src/test/resources/RyaShellTest-context.xml ---------------------------------------------------------------------- diff --git a/extras/rya.console/src/test/resources/RyaShellTest-context.xml b/extras/rya.console/src/test/resources/RyaShellTest-context.xml new file mode 100644 index 0000000..dfd075a --- /dev/null +++ b/extras/rya.console/src/test/resources/RyaShellTest-context.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> + + <!-- Tell Spring where it can find all of the Command components. --> + <context:component-scan base-package="mvm.rya.shell"/> + + <!-- Define the shell state bean that will be shared across all of the commands. --> + <bean id="sharedShellState" class="mvm.rya.shell.SharedShellState" /> + + <!-- We use a mock Password Prompt here to simulate a user entering a password. --> + <bean id="passwordPrompt" class="org.mockito.Mockito" factory-method="mock"> + <constructor-arg value="mvm.rya.shell.util.PasswordPrompt" /> + </bean> + + <!-- We use a mock Install Prompt here to simulate a user entering the installation configuration. --> + <bean id="installPrompt" class="org.mockito.Mockito" factory-method="mock"> + <constructor-arg value="mvm.rya.shell.util.InstallPrompt"/> + </bean> + + <!-- We use a mock SPARQL Prompt here to simulate a user entering the installation configuration. --> + <bean id="sparqlPrompt" class="org.mockito.Mockito" factory-method="mock"> + <constructor-arg value="mvm.rya.shell.util.SparqlPrompt"/> + </bean> + + <!-- Define each of the beans that hold onto commands used by the shell. --> + <bean id="ryaConnectionCommands" class="mvm.rya.shell.RyaConnectionCommands" /> + <bean id="ryaAdminCommands" class="mvm.rya.shell.RyaAdminCommands" /> +</beans> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.indexing.pcj/pom.xml ---------------------------------------------------------------------- diff --git a/extras/rya.indexing.pcj/pom.xml b/extras/rya.indexing.pcj/pom.xml index ee5ae07..c90ded3 100644 --- a/extras/rya.indexing.pcj/pom.xml +++ b/extras/rya.indexing.pcj/pom.xml @@ -43,6 +43,10 @@ under the License. <groupId>org.apache.rya</groupId> <artifactId>rya.api</artifactId> </dependency> + <dependency> + <groupId>org.apache.rya</groupId> + <artifactId>accumulo.rya</artifactId> + </dependency> <!-- Accumulo support dependencies. --> <dependency> @@ -80,5 +84,11 @@ under the License. <artifactId>rya.sail</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.rya</groupId> + <artifactId>accumulo.rya</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + </dependency> </dependencies> </project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PCJIdFactory.java ---------------------------------------------------------------------- diff --git a/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PCJIdFactory.java b/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PCJIdFactory.java new file mode 100644 index 0000000..0bba346 --- /dev/null +++ b/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PCJIdFactory.java @@ -0,0 +1,34 @@ +/** + * 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 org.apache.rya.indexing.pcj.storage; + +import java.util.UUID; + +/** + * Creates IDs for new Precomputed Joins. + */ +public class PCJIdFactory { + + /** + * @return The next ID to use for a PCJ. + */ + public String nextId() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/fcc98bd9/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PrecomputedJoinStorage.java ---------------------------------------------------------------------- diff --git a/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PrecomputedJoinStorage.java b/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PrecomputedJoinStorage.java index fb49b30..caaa8b6 100644 --- a/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PrecomputedJoinStorage.java +++ b/extras/rya.indexing.pcj/src/main/java/org/apache/rya/indexing/pcj/storage/PrecomputedJoinStorage.java @@ -19,13 +19,13 @@ package org.apache.rya.indexing.pcj.storage; import java.util.Collection; +import java.util.Iterator; import java.util.List; -import java.util.Set; import javax.annotation.ParametersAreNonnullByDefault; -import org.apache.rya.indexing.pcj.storage.accumulo.VariableOrder; import org.apache.rya.indexing.pcj.storage.accumulo.VisibilityBindingSet; +import org.openrdf.query.BindingSet; /** * Functions that create and maintain the PCJ tables that are used by Rya. @@ -36,7 +36,7 @@ public interface PrecomputedJoinStorage { /** * Get a list of all Precomputed Join indices that are being maintained. * - * @return The lkist of managed Precomputed Join indices. + * @return The list of managed Precomputed Join IDs. */ public List<String> listPcjs() throws PCJStorageException; @@ -47,7 +47,7 @@ public interface PrecomputedJoinStorage { * @param varOrders - The variable orders the results within the table will be written to. (not null) * @return A unique identifier for the index. */ - public String createPcj(final String sparql, final Set<VariableOrder> varOrders) throws PCJStorageException; + public String createPcj(final String sparql) throws PCJStorageException; /** * Get metadata about the Precomputed Join index. @@ -67,6 +67,16 @@ public interface PrecomputedJoinStorage { public void addResults(final String pcjId, final Collection<VisibilityBindingSet> results) throws PCJStorageException; /** + * Get an {@link Iterator} over the {@link BindingSet}s that are stored in the PCJ table. + * + * @param pcjId - Identifies the index the results will be read from. (not null) + * @return An iterator over all of the {@link BindingSet}s that are stored as + * results for the PCJ. + * @throws PCJStorageException The scan couldn't be performed. + */ + public Iterable<BindingSet> listResults(String pcjId) throws PCJStorageException; + + /** * Clears all values from a Precomputed Join index. The index will remain, * but all of its values will be removed. *
