org.bin01.db.verifier.ExternalClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.bin01.db.verifier.ExternalClassLoader.java

Source

/*
 * 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.bin01.db.verifier;

import io.tesla.aether.Repository;
import io.tesla.aether.TeslaAether;
import io.tesla.aether.internal.DefaultTeslaAether;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * 
 * Helper class to load external maven dependencies.
 *
 */
public class ExternalClassLoader {
    private static final Logger log = LoggerFactory.getLogger(ExternalClassLoader.class);
    private static final Map<String, URLClassLoader> loadersMap = Maps.newHashMap();
    private static final Set<String> exclusions = Sets.newHashSet();

    public synchronized static URLClassLoader getClassLoaderForCoordinates(VerifierConfig config,
            String coordinates, String className) {
        try {
            DefaultTeslaAether aether = getAetherClient(config);
            return getClassLoaderForCoordinates(aether, coordinates);
        } catch (DependencyResolutionException | MalformedURLException e) {
            throw Throwables.propagate(e);
        }
    }

    public synchronized static URLClassLoader getClassLoaderForCoordinates(TeslaAether aether, String coordinate)
            throws DependencyResolutionException, MalformedURLException {
        URLClassLoader loader = loadersMap.get(coordinate);
        if (loader == null) {
            final CollectRequest collectRequest = new CollectRequest();
            collectRequest.setRoot(new Dependency(new DefaultArtifact(coordinate), JavaScopes.RUNTIME));
            DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, DependencyFilterUtils
                    .andFilter(DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME), new DependencyFilter() {
                        @Override
                        public boolean accept(DependencyNode node, List<DependencyNode> parents) {
                            if (accept(node.getArtifact())) {
                                return false;
                            }

                            for (DependencyNode parent : parents) {
                                if (accept(parent.getArtifact())) {
                                    return false;
                                }
                            }

                            return true;
                        }

                        private boolean accept(final Artifact artifact) {
                            return exclusions.contains(artifact.getGroupId());
                        }
                    }));

            try {
                final List<Artifact> artifacts = aether.resolveArtifacts(dependencyRequest);

                List<URL> urls = Lists.newArrayListWithExpectedSize(artifacts.size());
                for (Artifact artifact : artifacts) {
                    if (!exclusions.contains(artifact.getGroupId())) {
                        urls.add(artifact.getFile().toURI().toURL());
                    } else {
                        log.debug("Skipped Artifact[{}]", artifact);
                    }
                }

                for (URL url : urls) {
                    log.info("Added URL[{}]", url);
                }

                loader = new URLClassLoader(urls.toArray(new URL[urls.size()]),
                        ExternalClassLoader.class.getClassLoader());
                loadersMap.put(coordinate, loader);
            } catch (Exception e) {
                log.error("Unable to resolve artifacts for [{}].", dependencyRequest, e);
                throw Throwables.propagate(e);
            }
        }
        return loader;
    }

    public static DefaultTeslaAether getAetherClient(VerifierConfig config) {
        /*
         * DefaultTeslaAether logs a bunch of stuff to System.out, which is annoying. We choose to
         * disable that unless debug logging is turned on. "Disabling" it, however, is kinda
         * bass-ackwards. We copy out a reference to the current System.out, and set System.out to a
         * noop output stream. Then after DefaultTeslaAether has pulled The reference we swap things
         * back.
         * 
         * This has implications for other things that are running in parallel to this. Namely, if
         * anything else also grabs a reference to System.out or tries to log to it while we have things
         * adjusted like this, then they will also log to nothingness. Fortunately, the code that calls
         * this is single-threaded and shouldn't hopefully be running alongside anything else that's
         * grabbing System.out. But who knows.
         */
        List<String> remoteUriList = config.getRemoteRepositories();

        List<Repository> remoteRepositories = Lists.newArrayList();
        for (String uri : remoteUriList) {
            try {
                URI u = new URI(uri);
                Repository r = new Repository(uri);

                if (u.getUserInfo() != null) {
                    String[] auth = u.getUserInfo().split(":", 2);
                    if (auth.length == 2) {
                        r.setUsername(auth[0]);
                        r.setPassword(auth[1]);
                    } else {
                        log.warn(
                                "Invalid credentials in repository URI, expecting [<user>:<password>], got [{}] for [{}]",
                                u.getUserInfo(), uri);
                    }
                }
                remoteRepositories.add(r);
            } catch (URISyntaxException e) {
                throw Throwables.propagate(e);
            }
        }

        if (log.isTraceEnabled() || log.isDebugEnabled()) {
            return new DefaultTeslaAether(config.getLocalRepository(),
                    remoteRepositories.toArray(new Repository[remoteRepositories.size()]));
        }

        PrintStream oldOut = System.out;
        try {
            System.setOut(new PrintStream(new OutputStream() {
                @Override
                public void write(int b) throws IOException {

                }

                @Override
                public void write(byte[] b) throws IOException {

                }

                @Override
                public void write(byte[] b, int off, int len) throws IOException {

                }
            }));
            return new DefaultTeslaAether(config.getLocalRepository(),
                    remoteRepositories.toArray(new Repository[remoteRepositories.size()]));
        } finally {
            System.setOut(oldOut);
        }
    }
}