/*
* 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 com.facebook.presto.verifier;
import com.facebook.presto.util.IterableTransformer;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import io.airlift.bootstrap.Bootstrap;
import io.airlift.bootstrap.LifeCycleManager;
import io.airlift.event.client.EventClient;
import org.skife.jdbi.v2.DBI;
import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.List;
import java.util.Set;
public class PrestoVerifier
{
protected PrestoVerifier()
{
}
public static void main(String[] args)
throws Exception
{
new PrestoVerifier().run(args);
}
public void run(String[] args)
throws Exception
{
if (args.length > 0) {
System.setProperty("config", args[0]);
}
ImmutableList.Builder<Module> builder = ImmutableList.<Module>builder()
.add(new PrestoVerifierModule())
.addAll(getAdditionalModules());
Bootstrap app = new Bootstrap(builder.build());
Injector injector = app.strictConfig().initialize();
VerifierConfig config = injector.getInstance(VerifierConfig.class);
injector.injectMembers(this);
EventClient eventClient = Iterables.getOnlyElement(injector.getInstance(Key.get(new TypeLiteral<Set<EventClient>>() {})));
VerifierDao dao = new DBI(config.getQueryDatabase()).onDemand(VerifierDao.class);
List<QueryPair> queries = dao.getQueriesBySuite(config.getSuite(), config.getMaxQueries());
queries = applyOverrides(config, queries);
queries = filterQueries(queries);
// Load jdbc drivers if needed
if (config.getAdditionalJdbcDriverPath() != null) {
List<URL> urlList = getUrls(config.getAdditionalJdbcDriverPath());
URL[] urls = new URL[urlList.size()];
urlList.toArray(urls);
if (config.getTestJdbcDriverName() != null) {
loadJdbcDriver(urls, config.getTestJdbcDriverName());
}
if (config.getControlJdbcDriverName() != null) {
loadJdbcDriver(urls, config.getControlJdbcDriverName());
}
}
// TODO: construct this with Guice
Verifier verifier = new Verifier(System.out, config, eventClient);
verifier.run(queries);
injector.getInstance(LifeCycleManager.class).stop();
}
private static void loadJdbcDriver(URL[] urls, String jdbcClassName)
{
try {
try (URLClassLoader classLoader = new URLClassLoader(urls)) {
Driver driver = (Driver) Class.forName(jdbcClassName, true, classLoader).getConstructor().newInstance();
// The code calling the DriverManager to load the driver needs to be in the same class loader as the driver
// In order to bypass this we create a shim that wraps the specified jdbc driver class.
// TODO: Change the implementation to be DataSource based instead of DriverManager based.
DriverManager.registerDriver(new ForwardingDriver(driver));
}
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
private static List<URL> getUrls(String path)
throws MalformedURLException
{
ImmutableList.Builder<URL> urlList = ImmutableList.builder();
File driverPath = new File(path);
if (!driverPath.isDirectory()) {
urlList.add(Paths.get(path).toUri().toURL());
return urlList.build();
}
File[] files = driverPath.listFiles(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return name.endsWith(".jar");
}
});
if (files == null) {
return urlList.build();
}
for (File file : files) {
// Does not handle nested directories
if (file.isDirectory()) {
continue;
}
urlList.add(Paths.get(file.getAbsolutePath()).toUri().toURL());
}
return urlList.build();
}
/**
* Override this method to apply additional filtering to queries, before they're run.
*/
protected List<QueryPair> filterQueries(List<QueryPair> queries)
{
return queries;
}
protected Iterable<Module> getAdditionalModules()
{
return ImmutableList.of();
}
private static List<QueryPair> applyOverrides(final VerifierConfig config, List<QueryPair> queries)
{
return IterableTransformer.on(queries).transform(new Function<QueryPair, QueryPair>()
{
@Override
public QueryPair apply(QueryPair input)
{
Query test = new Query(
Optional.fromNullable(config.getTestCatalogOverride()).or(input.getTest().getCatalog()),
Optional.fromNullable(config.getTestSchemaOverride()).or(input.getTest().getSchema()),
input.getTest().getQuery());
Query control = new Query(
Optional.fromNullable(config.getControlCatalogOverride()).or(input.getControl().getCatalog()),
Optional.fromNullable(config.getControlSchemaOverride()).or(input.getControl().getSchema()),
input.getControl().getQuery());
return new QueryPair(input.getSuite(), input.getName(), test, control);
}
}).list();
}
}