/* * Copyright 2015-2016 Red Hat, Inc, and individual contributors. * * 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 org.wildfly.swarm.fractions; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.wildfly.swarm.fractions.scanner.ClassAndPackageScanner; import org.wildfly.swarm.fractions.scanner.FilePresenceScanner; import org.wildfly.swarm.fractions.scanner.JarScanner; import org.wildfly.swarm.fractions.scanner.Scanner; import org.wildfly.swarm.fractions.scanner.WarScanner; import org.wildfly.swarm.fractions.scanner.WebXmlDescriptorScanner; import org.wildfly.swarm.spi.meta.FractionDetector; import org.wildfly.swarm.spi.meta.SimpleLogger; /** * @author Bob McWhirter * @author Toby Crawley * @author Ken Finnigan */ public class FractionUsageAnalyzer { public FractionUsageAnalyzer() { this(FractionList.get()); } public FractionUsageAnalyzer(final FractionList fractionList) { this.fractionList = fractionList; } public FractionUsageAnalyzer source(final Path source) { source(source.toFile()); return this; } public FractionUsageAnalyzer source(final File source) { this.sources.add(source); return this; } public FractionUsageAnalyzer logger(final SimpleLogger log) { this.log = log; return this; } public Collection<FractionDescriptor> detectNeededFractions() throws IOException { if (this.fractionList == null) { return Collections.emptySet(); } Set<FractionDescriptor> detectedFractions; loadDetectorsAndScanners(); sources.forEach(this::scanFile); Set<String> detectedFractionNames = detectors.stream() .filter(FractionDetector::wasDetected) .map(FractionDetector::artifactId) .distinct() .collect(Collectors.toSet()); if (sources.stream().anyMatch(e -> e.getName().endsWith(".war"))) { detectedFractionNames.add("undertow"); } detectedFractions = this.fractionList.getFractionDescriptors() .stream() .filter(fd -> detectedFractionNames.contains(fd.getArtifactId())) .collect(Collectors.toSet()); // Remove fractions that have an explicitDependency on each other Iterator<FractionDescriptor> it = detectedFractions.iterator(); while (it.hasNext()) { FractionDescriptor descriptor = it.next(); // Is set as a explicitDependency to any other descriptor? If so, remove it if (detectedFractions.stream().anyMatch(fd -> fd.getDependencies().contains(descriptor))) { it.remove(); } } // Add container only if no fractions are detected, as they have a transitive explicitDependency to container if (detectedFractions.isEmpty()) { detectedFractions.add(this.fractionList.getFractionDescriptor(FractionDescriptor.WILDFLY_SWARM_GROUP_ID, "container")); } return detectedFractions; } private boolean isZipFile(File source) { if (source.isDirectory()) { return false; } return source.getName().endsWith(".jar") || source.getName().endsWith(".war") || source.getName().endsWith(".zip"); } private void scanFile(File source) { if (isZipFile(source)) { fireScanner(suffix(source.getName()), zipFileScannerConsumer(source)); } else { if (source.isDirectory()) { if (source.getName().endsWith(".war") || new File(source, "WEB-INF").exists()) { fireScanner("war", explodedScannerConsumer(source)); } else { fireScanner("jar", explodedScannerConsumer(source)); } } else { fireScanner(suffix(source.getName()), explodedScannerConsumer(source)); } } } private Consumer<Scanner<?>> zipFileScannerConsumer(File source) { return s -> { try (ZipFile zip = new ZipFile(source)) { s.scan(zip, this::scanSource); } catch (IOException e) { log.error("", e); } }; } private Consumer<Scanner<?>> explodedScannerConsumer(File source) { return s -> { try { s.scan(source.toPath(), this::scanSource); } catch (IOException e) { log.error("", e); } }; } private void scanSource(final Path source) { final String suffix = suffix(source.getFileName().toString()); Collection<FractionDetector<?>> validDetectors = detectors.stream() .filter(d -> d.extensionToDetect().equals(suffix)) .filter(d -> !d.detectionComplete()) .collect(Collectors.toList()); if (validDetectors.size() > 0) { fireScanner(suffix, s -> { try (InputStream input = new FileInputStream(source.toFile())) { s.scan(source.getFileName().toString(), input, convertDetectors(validDetectors), this::scanFile); } catch (IOException e) { log.error("", e); } }); } } private void scanSource(ZipEntry entry, ZipFile source) { final String suffix = suffix(entry.getName()); Collection<FractionDetector<?>> validDetectors = detectors.stream() .filter(d -> d.extensionToDetect().equals(suffix)) .filter(d -> !d.detectionComplete()) .collect(Collectors.toList()); if (validDetectors.size() > 0) { fireScanner(suffix, s -> { try (InputStream input = source.getInputStream(entry)) { s.scan(entry.getName(), input, convertDetectors(validDetectors), this::scanFile); } catch (IOException e) { log.error("", e); } }); } } private <T> Collection<FractionDetector<T>> convertDetectors(Collection<FractionDetector<?>> untypedDetectors) { Collection<FractionDetector<T>> detectors = new HashSet<>(); untypedDetectors.forEach(d -> detectors.add(convert(d))); return detectors; } @SuppressWarnings("unchecked") private <T> FractionDetector<T> convert(FractionDetector<?> detector) { return (FractionDetector<T>) detector; } private void fireScanner(String suffix, Consumer<Scanner<?>> scannerConsumer) { List<Scanner<?>> scanners = this.scanners.stream() .filter(s -> s.extension().equals(suffix)) .collect(Collectors.toList()); scanners.forEach(scannerConsumer); } private String suffix(String name) { return name.substring(name.lastIndexOf('.') + 1); } private void loadDetectorsAndScanners() { if (detectorsLoaded) { return; } ServiceLoader<FractionDetector> detectorLoader = ServiceLoader.load(FractionDetector.class); detectorLoader.forEach(d -> detectors.add(d)); scanners.add(new WarScanner()); scanners.add(new JarScanner()); scanners.add(new ClassAndPackageScanner()); scanners.add(new WebXmlDescriptorScanner()); scanners.add(new FilePresenceScanner()); ClassAndPackageScanner.classesPackagesAlreadyDetected.clear(); detectorsLoaded = true; } private final List<File> sources = new ArrayList<>(); private final FractionList fractionList; private Collection<FractionDetector<?>> detectors = new HashSet<>(); private Collection<Scanner<?>> scanners = new HashSet<>(); private boolean detectorsLoaded = false; private SimpleLogger log = new SimpleLogger() { }; }