/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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.asakusafw.directio.hive.tools.cli;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.asakusafw.directio.hive.info.TableInfo;
import com.asakusafw.directio.hive.syntax.HiveCreateTable;
import com.asakusafw.directio.hive.syntax.HiveQlEmitter;
/**
* Generate HiveQL for {@link HiveCreateTable}.
* @since 0.7.0
*/
public class GenerateCreateTableTask {
static final Charset ENCODING = StandardCharsets.UTF_8;
static final Logger LOG = LoggerFactory.getLogger(GenerateCreateTableTask.class);
private static final String STATEMENT_SEPARATOR = ";\n"; //$NON-NLS-1$
/**
* Performs this task.
* @param configuration the task configuration
* @throws IOException if failed by I/O error
*/
public void perform(Configuration configuration) throws IOException {
ClassCollector collector = collect(configuration);
for (File file : configuration.sources) {
LOG.info(MessageFormat.format(
Messages.getString("GenerateCreateTableTask.infoInspectClassPath"), //$NON-NLS-1$
file));
collector.inspect(file);
}
int count = 0;
try (Writer writer = open(configuration)) {
for (Class<?> aClass : collector.getClasses()) {
TableInfo schema = getSchema(aClass);
if (schema == null) {
continue;
}
LOG.info(MessageFormat.format(
Messages.getString("GenerateCreateTableTask.infoStartGenerateDdl"), //$NON-NLS-1$
schema.getName()));
HiveQlEmitter.emit(new Ql(schema, configuration), writer);
writer.write(STATEMENT_SEPARATOR);
count++;
}
}
LOG.info(MessageFormat.format(
Messages.getString("GenerateCreateTableTask.infoFinishGenerateDdl"), //$NON-NLS-1$
count,
configuration.output));
}
private Writer open(Configuration configuration) throws IOException {
File file = configuration.output;
File parent = file.getParentFile();
if (parent.mkdirs() == false && parent.isDirectory() == false) {
throw new IOException(MessageFormat.format(
Messages.getString("GenerateCreateTableTask.errorFailedToCreateOutputDirectory"), //$NON-NLS-1$
file));
}
return new OutputStreamWriter(new FileOutputStream(file), ENCODING);
}
private ClassCollector collect(Configuration configuration) {
ClassCollector collector = new ClassCollector(configuration.classLoader, aClass -> {
TableInfo info = getSchema(aClass);
if (info == null) {
return false;
}
if (configuration.acceptTableNames != null) {
if (configuration.acceptTableNames.matcher(info.getName()).matches() == false) {
LOG.debug("filtered table: {}", info.getName()); //$NON-NLS-1$
return false;
}
}
return true;
});
return collector;
}
static TableInfo getSchema(Class<?> aClass) {
if (TableInfo.Provider.class.isAssignableFrom(aClass) == false
|| Modifier.isInterface(aClass.getModifiers())
|| Modifier.isAbstract(aClass.getModifiers())) {
return null;
}
try {
TableInfo.Provider provider = aClass.asSubclass(TableInfo.Provider.class).newInstance();
return provider.getSchema();
} catch (Exception e) {
LOG.warn(MessageFormat.format(
Messages.getString("GenerateCreateTableTask.warnFailedToInstantiate"), //$NON-NLS-1$
aClass.getName()), e);
return null;
}
}
/**
* Configuration for {@link GenerateCreateTableTask}.
* @since 0.7.0
*/
public static final class Configuration {
final ClassLoader classLoader;
final List<File> sources;
final Pattern acceptTableNames;
final Function<? super TableInfo, ? extends String> locationProvider;
final String databaseName;
final File output;
/**
* Creates a new instance.
* @param classLoader the target class loader
* @param sources the class path (nullable)
* @param acceptTableNames target table name pattern (nullable)
* @param locationProvider provides table location (nullable)
* @param databaseName the database name (nullable)
* @param output the output path
*/
public Configuration(
ClassLoader classLoader,
List<File> sources,
Pattern acceptTableNames,
Function<? super TableInfo, ? extends String> locationProvider,
String databaseName,
File output) {
this.classLoader = classLoader;
this.sources = sources;
this.acceptTableNames = acceptTableNames;
this.locationProvider = locationProvider;
this.databaseName = databaseName;
this.output = output;
}
}
private static final class Ql implements HiveCreateTable {
private final TableInfo table;
private final Configuration configuration;
Ql(TableInfo table, Configuration configuration) {
this.table = table;
this.configuration = configuration;
}
@Override
public TableInfo getTableInfo() {
return table;
}
@Override
public boolean isExternal() {
return true;
}
@Override
public boolean isSkipPresentTable() {
return true;
}
@Override
public String getDatabaseName() {
return configuration.databaseName;
}
@Override
public String getLocation() {
if (configuration.locationProvider == null) {
return null;
}
return configuration.locationProvider.apply(table);
}
}
}