/*
* Copyright © 2014-2016 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.explore.service;
import co.cask.cdap.common.conf.CConfiguration;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.guice.ConfigModule;
import co.cask.cdap.common.guice.DiscoveryRuntimeModule;
import co.cask.cdap.common.guice.IOModule;
import co.cask.cdap.common.guice.LocationRuntimeModule;
import co.cask.cdap.common.namespace.guice.NamespaceClientRuntimeModule;
import co.cask.cdap.data.runtime.DataFabricModules;
import co.cask.cdap.data.runtime.DataSetServiceModules;
import co.cask.cdap.data.runtime.DataSetsModules;
import co.cask.cdap.data.stream.StreamAdminModules;
import co.cask.cdap.data.view.ViewAdminModules;
import co.cask.cdap.data2.datafabric.dataset.service.DatasetService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetOpExecutor;
import co.cask.cdap.explore.guice.ExploreClientModule;
import co.cask.cdap.explore.guice.ExploreRuntimeModule;
import co.cask.cdap.metrics.guice.MetricsClientRuntimeModule;
import co.cask.cdap.notifications.feeds.NotificationFeedManager;
import co.cask.cdap.notifications.feeds.service.NoOpNotificationFeedManager;
import co.cask.cdap.proto.ColumnDesc;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.QueryHandle;
import co.cask.cdap.proto.QueryResult;
import co.cask.cdap.proto.QueryStatus;
import co.cask.cdap.store.guice.NamespaceStoreModule;
import co.cask.cdap.test.SlowTests;
import co.cask.tephra.TransactionManager;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.hadoop.conf.Configuration;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
*
*/
@Category(SlowTests.class)
public class InMemoryExploreServiceTest {
@ClassRule
public static TemporaryFolder tmpFolder = new TemporaryFolder();
private static TransactionManager transactionManager;
private static ExploreService exploreService;
private static DatasetOpExecutor dsOpService;
private static DatasetService datasetService;
@BeforeClass
public static void start() throws Exception {
CConfiguration configuration = CConfiguration.create();
Configuration hConf = new Configuration();
configuration.set(Constants.CFG_DATA_INMEMORY_PERSISTENCE, Constants.InMemoryPersistenceType.MEMORY.name());
configuration.set(Constants.Explore.LOCAL_DATA_DIR, tmpFolder.newFolder().getAbsolutePath());
Injector injector = Guice.createInjector(
new ConfigModule(configuration, hConf),
new IOModule(),
new DiscoveryRuntimeModule().getInMemoryModules(),
new LocationRuntimeModule().getInMemoryModules(),
new DataFabricModules().getInMemoryModules(),
new DataSetsModules().getStandaloneModules(),
new DataSetServiceModules().getInMemoryModules(),
new MetricsClientRuntimeModule().getInMemoryModules(),
new ExploreRuntimeModule().getInMemoryModules(),
new ExploreClientModule(),
new ViewAdminModules().getInMemoryModules(),
new StreamAdminModules().getInMemoryModules(),
new NamespaceClientRuntimeModule().getInMemoryModules(),
new NamespaceStoreModule().getStandaloneModules(),
new AbstractModule() {
@Override
protected void configure() {
bind(NotificationFeedManager.class).to(NoOpNotificationFeedManager.class);
}
});
transactionManager = injector.getInstance(TransactionManager.class);
transactionManager.startAndWait();
dsOpService = injector.getInstance(DatasetOpExecutor.class);
dsOpService.startAndWait();
datasetService = injector.getInstance(DatasetService.class);
datasetService.startAndWait();
exploreService = injector.getInstance(ExploreService.class);
exploreService.startAndWait();
}
@AfterClass
public static void stop() throws Exception {
exploreService.stop();
datasetService.stopAndWait();
dsOpService.stopAndWait();
transactionManager.stopAndWait();
}
@Test
public void testHiveIntegration() throws Exception {
String otherNamespace = "otherNamespace";
waitForCompletionStatus(exploreService.createNamespace(Id.Namespace.from(otherNamespace)));
runCleanup(ImmutableList.of(Id.Namespace.DEFAULT.getId(), otherNamespace));
runNamespacedTest(Id.Namespace.DEFAULT.getId());
runNamespacedTest(otherNamespace);
runCleanup(ImmutableList.of(Id.Namespace.DEFAULT.getId(), otherNamespace));
waitForCompletionStatus(exploreService.deleteNamespace(Id.Namespace.from(otherNamespace)));
}
private void runNamespacedTest(String namespace) throws Exception {
URL loadFileUrl = getClass().getResource("/test_table.dat");
Assert.assertNotNull(loadFileUrl);
// Should have no tables
runCommand(namespace, "show tables",
true,
Lists.newArrayList(new ColumnDesc("tab_name", "STRING", 1, "from deserializer")),
ImmutableList.<QueryResult>of());
runCommand(namespace, "create table test (first INT, second STRING) ROW FORMAT " +
"DELIMITED FIELDS TERMINATED BY '\\t'",
false,
ImmutableList.<ColumnDesc>of(),
ImmutableList.<QueryResult>of()
);
runCommand(namespace, "show tables",
true,
Lists.newArrayList(new ColumnDesc("tab_name", "STRING", 1, "from deserializer")),
Lists.newArrayList(new QueryResult(Lists.<Object>newArrayList("test"))));
runCommand(namespace, "describe test",
true,
Lists.newArrayList(
new ColumnDesc("col_name", "STRING", 1, "from deserializer"),
new ColumnDesc("data_type", "STRING", 2, "from deserializer"),
new ColumnDesc("comment", "STRING", 3, "from deserializer")
),
Lists.newArrayList(
new QueryResult(Lists.<Object>newArrayList("first", "int", "")),
new QueryResult(Lists.<Object>newArrayList("second", "string", ""))
)
);
// Should have no data
runCommand(namespace, "select * from test",
true,
Lists.newArrayList(new ColumnDesc("test.first", "INT", 1, null),
new ColumnDesc("test.second", "STRING", 2, null)),
ImmutableList.<QueryResult>of());
runCommand(namespace, "LOAD DATA LOCAL INPATH '"
+ new File(loadFileUrl.toURI()).getAbsolutePath() + "' INTO TABLE test",
false,
ImmutableList.<ColumnDesc>of(),
ImmutableList.<QueryResult>of()
);
runCommand(namespace, "select first, second from test",
true,
Lists.newArrayList(new ColumnDesc("first", "INT", 1, null),
new ColumnDesc("second", "STRING", 2, null)),
Lists.newArrayList(
new QueryResult(Lists.<Object>newArrayList("1", "one")),
new QueryResult(Lists.<Object>newArrayList("2", "two")),
new QueryResult(Lists.<Object>newArrayList("3", "three")),
new QueryResult(Lists.<Object>newArrayList("4", "four")),
new QueryResult(Lists.<Object>newArrayList("5", "five")))
);
runCommand(namespace, "select * from test",
true,
Lists.newArrayList(new ColumnDesc("test.first", "INT", 1, null),
new ColumnDesc("test.second", "STRING", 2, null)),
Lists.newArrayList(
new QueryResult(Lists.<Object>newArrayList("1", "one")),
new QueryResult(Lists.<Object>newArrayList("2", "two")),
new QueryResult(Lists.<Object>newArrayList("3", "three")),
new QueryResult(Lists.<Object>newArrayList("4", "four")),
new QueryResult(Lists.<Object>newArrayList("5", "five"))));
}
private void runCleanup(List<String> namespaces) throws Exception {
for (String namespace : namespaces) {
runCommand(namespace, "drop table if exists test",
false,
ImmutableList.<ColumnDesc>of(),
ImmutableList.<QueryResult>of());
}
}
private static void runCommand(String namespace, String command, boolean expectedHasResult,
List<ColumnDesc> expectedColumnDescs,
List<QueryResult> expectedResults) throws Exception {
QueryHandle handle = exploreService.execute(Id.Namespace.from(namespace), command);
QueryStatus status = waitForCompletionStatus(handle);
Assert.assertEquals(QueryStatus.OpStatus.FINISHED, status.getStatus());
Assert.assertEquals(expectedHasResult, status.hasResults());
Assert.assertEquals(expectedColumnDescs, exploreService.getResultSchema(handle));
Assert.assertEquals(expectedResults.toString(),
trimColumnValues(exploreService.nextResults(handle, 100)).toString());
exploreService.close(handle);
}
private static List<QueryResult> trimColumnValues(List<QueryResult> results) {
List<QueryResult> newResults = Lists.newArrayList();
for (QueryResult result : results) {
List<Object> newCols = Lists.newArrayList();
for (Object obj : result.getColumns()) {
if (obj instanceof String) {
newCols.add(((String) obj).trim());
} else {
newCols.add(obj);
}
}
newResults.add(new QueryResult(newCols));
}
return newResults;
}
private static QueryStatus waitForCompletionStatus(QueryHandle handle) throws Exception {
QueryStatus status;
do {
TimeUnit.MILLISECONDS.sleep(200);
status = exploreService.getStatus(handle);
} while (status.getStatus() == QueryStatus.OpStatus.RUNNING ||
status.getStatus() == QueryStatus.OpStatus.PENDING);
return status;
}
}