blob: 4c65eb35281739807af5cc14109878fb1f7a68d6 [file] [log] [blame]
/*
* 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.drill.common.scanner;
import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Set;
import org.apache.drill.common.config.ConfigConstants;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.common.scanner.persistence.ScanResult;
import org.apache.drill.common.util.JacksonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
/**
* main class to integrate classpath scanning in the build.
* @see BuildTimeScan#main(String[])
*/
public class BuildTimeScan {
private static final Logger logger = LoggerFactory.getLogger(BuildTimeScan.class);
private static final String REGISTRY_FILE = "META-INF/drill-module-scan/registry.json";
private static final ObjectMapper mapper = JacksonUtils.createObjectMapper().enable(INDENT_OUTPUT);
private static final ObjectReader reader = mapper.readerFor(ScanResult.class);
private static final ObjectWriter writer = mapper.writerFor(ScanResult.class);
/**
* @return paths that have the prescanned registry file in them
*/
static Set<URL> getPrescannedPaths() {
return ClassPathScanner.forResource(REGISTRY_FILE, true);
}
/**
* loads all the prescanned resources from classpath
* @return the result of the previous scan
*/
static ScanResult load() {
return loadExcept(null);
}
/**
* loads all the prescanned resources from classpath
* (except for the target location in case it already exists)
* @return the result of the previous scan
*/
private static ScanResult loadExcept(URL ignored) {
Set<URL> preScanned = ClassPathScanner.forResource(REGISTRY_FILE, false);
ScanResult result = null;
for (URL u : preScanned) {
if (ignored!= null && u.toString().startsWith(ignored.toString())) {
continue;
}
try (InputStream reflections = u.openStream()) {
ScanResult ref = reader.readValue(reflections);
if (result == null) {
result = ref;
} else {
result = result.merge(ref);
}
} catch (IOException e) {
throw new DrillRuntimeException("can't read function registry at " + u, e);
}
}
if (result != null) {
if (logger.isInfoEnabled()) {
StringBuilder sb = new StringBuilder();
sb.append(format("Loaded prescanned packages %s from locations:\n", result.getScannedPackages()));
for (URL u : preScanned) {
sb.append('\t');
sb.append(u.toExternalForm());
sb.append('\n');
}
}
logger.info(format("Loaded prescanned packages %s from locations %s", result.getScannedPackages(), preScanned));
return result;
} else {
return ClassPathScanner.emptyResult();
}
}
private static void save(ScanResult scanResult, File file) {
try {
writer.writeValue(file, scanResult);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* to generate the prescan file during build
* @param args the root path for the classes where {@link BuildTimeScan#REGISTRY_FILE} is generated
* @throws Exception
*/
public static void main(String[] args) throws Exception {
if (args.length != 1) {
throw new IllegalArgumentException("Usage: java {cp} " + BuildTimeScan.class.getName() + " path/to/scan");
}
String basePath = args[0];
logger.info("Scanning: {}", basePath);
File registryFile = new File(basePath, REGISTRY_FILE);
File dir = registryFile.getParentFile();
if ((!dir.exists() && !dir.mkdirs()) || !dir.isDirectory()) {
throw new IllegalArgumentException("could not create dir " + dir.getAbsolutePath());
}
DrillConfig config = DrillConfig.create();
// normalize
if (!basePath.endsWith("/")) {
basePath = basePath + "/";
}
if (!basePath.startsWith("/")) {
basePath = "/" + basePath;
}
URL url = new URL("file:" + basePath);
Set<URL> markedPaths = ClassPathScanner.getMarkedPaths(ConfigConstants.DRILL_JAR_MARKER_FILE_RESOURCE_PATHNAME);
if (!markedPaths.contains(url)) {
throw new IllegalArgumentException(url + " not in " + markedPaths);
}
List<String> packagePrefixes = ClassPathScanner.getPackagePrefixes(config);
List<String> baseClasses = ClassPathScanner.getScannedBaseClasses(config);
List<String> scannedAnnotations = ClassPathScanner.getScannedAnnotations(config);
ScanResult preScanned = loadExcept(url);
ScanResult scan = ClassPathScanner.scan(
asList(url),
packagePrefixes,
baseClasses,
scannedAnnotations,
preScanned);
save(scan, registryFile);
}
}