blob: d0c018fb685af0548ba7d0d62901ca477384d081 [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.persistence;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Stopwatch;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The root doc of the scan result
*/
public final class ScanResult {
private static final Logger logger = LoggerFactory.getLogger(ScanResult.class);
private final List<String> scannedPackages;
private final Set<String> scannedClasses;
private final Set<String> scannedAnnotations;
private final List<AnnotatedClassDescriptor> annotatedClasses;
private final List<ParentClassDescriptor> implementations;
private final Map<String, ParentClassDescriptor> parentClassByName;
private final Multimap<String, AnnotatedClassDescriptor> annotationsByName;
@JsonCreator public ScanResult(
@JsonProperty("scannedPackages") Collection<String> scannedPackages,
@JsonProperty("scannedClasses") Collection<String> scannedClasses,
@JsonProperty("scannedAnnotations") Collection<String> scannedAnnotations,
@JsonProperty("annotatedClasses") Collection<AnnotatedClassDescriptor> annotatedClasses,
@JsonProperty("implementations") Collection<ParentClassDescriptor> implementations) {
this.scannedPackages = unmodifiableList(new ArrayList<>(checkNotNull(scannedPackages)));
this.scannedClasses = unmodifiableSet(new HashSet<>(checkNotNull(scannedClasses)));
this.scannedAnnotations = unmodifiableSet(new HashSet<>(checkNotNull(scannedAnnotations)));
this.annotatedClasses = unmodifiableList(new ArrayList<>(checkNotNull(annotatedClasses)));
this.implementations = unmodifiableList(new ArrayList<>(checkNotNull(implementations)));
this.parentClassByName = new HashMap<>();
for (ParentClassDescriptor parentClassDescriptor : implementations) {
this.parentClassByName.put(parentClassDescriptor.getName(), parentClassDescriptor);
}
this.annotationsByName = HashMultimap.create();
for (AnnotatedClassDescriptor annotated : annotatedClasses) {
for (AnnotationDescriptor annotationDescriptor : annotated.getAnnotations()) {
if (scannedAnnotations.contains(annotationDescriptor.getAnnotationType())) {
this.annotationsByName.put(annotationDescriptor.getAnnotationType(), annotated);
}
}
}
}
/**
* @return packages that were scanned as defined in the drill configs
*/
public List<String> getScannedPackages() {
return scannedPackages;
}
/**
* @return functions annotated with configured annotations
*/
public List<AnnotatedClassDescriptor> getAnnotatedClasses() {
return annotatedClasses;
}
/**
* @return list of implementations per parent class
*/
public List<ParentClassDescriptor> getImplementations() {
return implementations;
}
public Set<String> getScannedClasses() {
return scannedClasses;
}
public Set<String> getScannedAnnotations() {
return scannedAnnotations;
}
/**
* @param c the parent class name
* @return the descriptor for the implementations found
*/
public ParentClassDescriptor getImplementations(String c) {
if (!scannedClasses.contains(c)) {
throw new IllegalArgumentException(
c + " is not scanned. "
+ "Only implementations for the following classes are scanned: "
+ scannedClasses);
}
return parentClassByName.get(c);
}
/**
* Loads all the scanned classes for this parent as a side effect
* @param c the parent
* @return all the classes found
*/
public <T> Set<Class<? extends T>> getImplementations(Class<T> c) {
ParentClassDescriptor p = getImplementations(c.getName());
Stopwatch watch = Stopwatch.createStarted();
Set<Class<? extends T>> result = new HashSet<>();
try {
if (p != null) {
for (ChildClassDescriptor child : p.getChildren()) {
if (!child.isAbstract()) {
try {
result.add(Class.forName(child.getName()).asSubclass(c));
} catch (ClassNotFoundException e) {
throw new DrillRuntimeException("scanned class could not be found: " + child.getName(), e);
}
}
}
}
return result;
} finally {
logger.info(
format("loading %d classes for %s took %dms",
result.size(), c.getName(), watch.elapsed(MILLISECONDS)));
}
}
/**
* @param c the annotation class name
* @return the descriptor of the annotated class
*/
public List<AnnotatedClassDescriptor> getAnnotatedClasses(String c) {
if (!scannedAnnotations.contains(c)) {
throw new IllegalArgumentException(
c + " is not scanned. "
+ "Only the following Annotations are scanned: "
+ scannedAnnotations);
}
return new ArrayList<>(annotationsByName.get(c));
}
@Override
public String toString() {
return "ScanResult ["
+ "scannedPackages=" + scannedPackages + ", "
+ "scannedClasses=" + scannedClasses + ", "
+ "scannedAnnotations=" + scannedAnnotations + ", "
+ "annotatedClasses=" + annotatedClasses + ", "
+ "implementations=" + implementations + "]";
}
private <T> List<T> merge(Collection<T> a, Collection<T> b) {
final List<T> newList = new ArrayList<>(a);
newList.addAll(b);
return newList;
}
/**
* merges this and other together into a new result object
* @param other
* @return the resulting merge
*/
public ScanResult merge(ScanResult other) {
final Multimap<String, ChildClassDescriptor> newImpls = HashMultimap.create();
for (Collection<ParentClassDescriptor> impls : asList(implementations, other.implementations)) {
for (ParentClassDescriptor c : impls) {
newImpls.putAll(c.getName(), c.getChildren());
}
}
List<ParentClassDescriptor> newImplementations = new ArrayList<>();
for (Entry<String, Collection<ChildClassDescriptor>> entry : newImpls.asMap().entrySet()) {
newImplementations.add(new ParentClassDescriptor(entry.getKey(), new ArrayList<>(entry.getValue())));
}
return new ScanResult(
merge(scannedPackages, other.scannedPackages),
merge(scannedClasses, other.scannedClasses),
merge(scannedAnnotations, other.scannedAnnotations),
merge(annotatedClasses, other.annotatedClasses),
newImplementations);
}
}