/* * Copyright 2016 ANI Technologies Pvt. Ltd. * * 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.olacabs.fabric.compute.builder.impl; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.olacabs.fabric.compute.processor.ProcessorBase; import com.olacabs.fabric.compute.source.PipelineSource; import com.olacabs.fabric.model.common.ComponentMetadata; import com.olacabs.fabric.model.common.ComponentType; import com.olacabs.fabric.model.processor.Processor; import com.olacabs.fabric.model.source.Source; import lombok.Builder; import lombok.Data; import org.apache.http.conn.UnsupportedSchemeException; import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ConfigurationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.*; import java.util.stream.Collectors; /** * TODO javadoc. */ public class JarScanner { private static final Logger LOGGER = LoggerFactory.getLogger(DownloadingLoader.class); private static final String PROPERTIES_FILE_NAME = "compute.properties"; private Properties properties; public JarScanner() throws Exception { this.properties = new Properties(); this.properties.load(JarScanner.class.getResourceAsStream("/" + PROPERTIES_FILE_NAME)); } private URL[] genUrls(URL[] jarFileURLs) { URLClassLoader loader = (URLClassLoader) ClassLoader.getSystemClassLoader(); URL[] originalURLs = loader.getURLs(); Set<URL> mergedJarURLs = new HashSet<URL>(originalURLs.length + jarFileURLs.length); mergedJarURLs.addAll(Arrays.asList(originalURLs)); mergedJarURLs.addAll(Arrays.asList(jarFileURLs)); return mergedJarURLs.toArray(new URL[mergedJarURLs.size()]); } private ClassLoader createClassLoader(URL[] urls) { URLClassLoader l = new URLClassLoader(urls); return l; } public List<ScanResult> loadJars(final Collection<String> urls, ClassLoader parentLoader) throws Exception { //URLClassLoader child = new URLClassLoader(download(urls), this.getClass().getClassLoader()); //URLClassLoader child = new URLClassLoader(download(urls), parentLoader); URL[] downloadedUrls = genUrls(download(urls)); ClassLoader child = createClassLoader(downloadedUrls); // Evil hack Thread.currentThread().setContextClassLoader(child); return ImmutableList.<ScanResult>builder().addAll(scanForProcessors(child, downloadedUrls)) .addAll(scanForSources(child, downloadedUrls)).build(); } URL[] download(Collection<String> urls) { ArrayList<URL> downloadedURLs = urls.stream().map(url -> { URI uri = URI.create(url); String downloaderImplClassName = properties.getProperty(String.format("fs.%s.impl", uri.getScheme())); if (null == downloaderImplClassName) { throw new RuntimeException( new UnsupportedSchemeException(uri.getScheme() + " is not supported for downloading jars")); } try { Class clazz = Class.forName(downloaderImplClassName); if (JarDownloader.class.isAssignableFrom(clazz)) { try { return ((JarDownloader) clazz.newInstance()).download(url).toUri().toURL(); } catch (Exception e) { throw new RuntimeException(e); } } else { throw new RuntimeException("Unsupported implementation " + downloaderImplClassName + " of " + JarDownloader.class.getSimpleName()); } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } }) .collect(Collectors.toCollection(ArrayList::new)); return downloadedURLs.toArray(new URL[downloadedURLs.size()]); } private List<ScanResult> scanForProcessors(ClassLoader classLoader, URL[] downloadedUrls) throws Exception { Reflections reflections = new Reflections(new ConfigurationBuilder().addClassLoader(classLoader) .addScanners(new SubTypesScanner(), new TypeAnnotationsScanner()).addUrls(downloadedUrls)); Set<Class<?>> processors = Sets.intersection(reflections.getTypesAnnotatedWith(Processor.class), reflections.getSubTypesOf(ProcessorBase.class)); return processors.stream().map(processor -> { Processor processorInfo = processor.getAnnotation(Processor.class); ComponentMetadata metadata = ComponentMetadata.builder().type(ComponentType.PROCESSOR).namespace(processorInfo.namespace()) .name(processorInfo.name()).version(processorInfo.version()) .description(processorInfo.description()).cpu(processorInfo.cpu()) .memory(processorInfo.memory()).processorType(processorInfo.processorType()) .requiredProperties(ImmutableList.copyOf(processorInfo.requiredProperties())) .optionalProperties(ImmutableList.copyOf(processorInfo.optionalProperties())).build(); return ScanResult.builder().metadata(metadata).componentClass(processor).build(); }).collect(Collectors.toCollection(ArrayList::new)); } private List<ScanResult> scanForSources(ClassLoader classLoader, URL[] downloadedUrls) throws Exception { Reflections reflections = new Reflections(new ConfigurationBuilder().addClassLoader(classLoader) .addScanners(new SubTypesScanner(), new TypeAnnotationsScanner()).addUrls(downloadedUrls)); Set<Class<?>> sources = Sets.intersection(reflections.getTypesAnnotatedWith(Source.class), reflections.getSubTypesOf(PipelineSource.class)); return sources.stream().map(source -> { Source sourceInfo = source.getAnnotation(Source.class); ComponentMetadata metadata = ComponentMetadata.builder().type(ComponentType.SOURCE).namespace(sourceInfo.namespace()) .name(sourceInfo.name()).version(sourceInfo.version()).description(sourceInfo.description()) .cpu(sourceInfo.cpu()).memory(sourceInfo.memory()) .requiredProperties(ImmutableList.copyOf(sourceInfo.requiredProperties())) .optionalProperties(ImmutableList.copyOf(sourceInfo.optionalProperties())).build(); return ScanResult.builder().metadata(metadata).componentClass(source).build(); }).collect(Collectors.toCollection(ArrayList::new)); } /** * Scan result class. */ @Builder @Data public static class ScanResult { private ComponentMetadata metadata; private Class<?> componentClass; } }