001/**
002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.runtime.manager.service;
017
018import static java.util.Optional.ofNullable;
019import static java.util.stream.Collectors.toList;
020
021import java.io.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.ObjectStreamException;
025import java.io.Serializable;
026import java.net.MalformedURLException;
027import java.net.URL;
028import java.nio.file.Files;
029import java.nio.file.Path;
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.function.Function;
033
034import org.talend.sdk.component.api.service.dependency.Resolver;
035import org.talend.sdk.component.classloader.ConfigurableClassLoader;
036import org.talend.sdk.component.dependencies.maven.Artifact;
037import org.talend.sdk.component.dependencies.maven.MvnDependencyListLocalRepositoryResolver;
038import org.talend.sdk.component.runtime.serialization.SerializableService;
039
040import lombok.RequiredArgsConstructor;
041
042@RequiredArgsConstructor
043public class ResolverImpl implements Resolver, Serializable {
044
045    private final String plugin;
046
047    private final Function<String, Path> fileResolver;
048
049    @Override
050    public ClassLoaderDescriptor mapDescriptorToClassLoader(final InputStream descriptor) {
051        final Collection<URL> urls = new ArrayList<>();
052        final Collection<String> nested = new ArrayList<>();
053        final Collection<String> resolved = new ArrayList<>();
054        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
055        final ClassLoader loader =
056                ofNullable(classLoader).map(ClassLoader::getParent).orElseGet(ClassLoader::getSystemClassLoader);
057        try {
058            new MvnDependencyListLocalRepositoryResolver(null, fileResolver)
059                    .resolveFromDescriptor(descriptor)
060                    .forEach(artifact -> {
061                        final String path = artifact.toPath();
062                        final Path file = fileResolver.apply(path);
063                        if (Files.exists(file)) {
064                            try {
065                                urls.add(file.toUri().toURL());
066                                resolved.add(artifact.toCoordinate());
067                            } catch (final MalformedURLException e) {
068                                throw new IllegalStateException(e);
069                            }
070                        } else if (loader.getResource("MAVEN-INF/repository/" + path) != null) {
071                            nested.add(path);
072                            resolved.add(artifact.toCoordinate());
073                        } // else will be missing
074                    });
075            final ConfigurableClassLoader volatileLoader = new ConfigurableClassLoader(plugin + "#volatile-resolver",
076                    urls.toArray(new URL[0]), classLoader, it -> false, it -> true, nested.toArray(new String[0]),
077                    ConfigurableClassLoader.class.isInstance(classLoader)
078                            ? ConfigurableClassLoader.class.cast(classLoader).getJvmMarkers()
079                            : new String[] { "" });
080            return new ClassLoaderDescriptorImpl(volatileLoader, resolved);
081        } catch (final IOException e) {
082            throw new IllegalArgumentException(e);
083        }
084    }
085
086    @Override
087    public Collection<File> resolveFromDescriptor(final InputStream descriptor) {
088        try {
089            return new MvnDependencyListLocalRepositoryResolver(null, fileResolver)
090                    .resolveFromDescriptor(descriptor)
091                    .map(Artifact::toPath)
092                    .map(fileResolver)
093                    .map(Path::toFile)
094                    .collect(toList());
095        } catch (final IOException e) {
096            throw new IllegalArgumentException(e);
097        }
098    }
099
100    Object writeReplace() throws ObjectStreamException {
101        return new SerializableService(plugin, Resolver.class.getName());
102    }
103
104    @RequiredArgsConstructor
105    private static class ClassLoaderDescriptorImpl implements ClassLoaderDescriptor {
106
107        private final ConfigurableClassLoader volatileLoader;
108
109        private final Collection<String> resolved;
110
111        @Override
112        public ClassLoader asClassLoader() {
113            return volatileLoader;
114        }
115
116        @Override
117        public Collection<String> resolvedDependencies() {
118            return resolved;
119        }
120
121        @Override
122        public void close() throws Exception {
123            volatileLoader.close();
124        }
125    }
126}