| /* |
| * 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); |
| } |
| } |