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 java.net.URI;
019import java.nio.charset.StandardCharsets;
020import java.nio.file.Files;
021import java.nio.file.Path;
022import java.nio.file.Paths;
023import java.util.Objects;
024import java.util.Optional;
025import java.util.regex.Matcher;
026import java.util.stream.Stream;
027
028import org.talend.sdk.component.runtime.manager.service.path.PathHandler;
029import org.talend.sdk.component.runtime.manager.service.path.PathHandlerImpl;
030
031import lombok.Data;
032import lombok.NoArgsConstructor;
033import lombok.extern.slf4j.Slf4j;
034
035/**
036 * m2 discovery process is used for plugins/connectors loading.
037 *
038 * The default discovery process is at it follows:
039 * <ul>
040 * <li>read {@code talend.component.manager.m2.repository} system property</li>
041 * <li>check if we're running in studio and uses its m2 if system property is not set to maven.repository=global</li>
042 * <li>parse maven's {@code settings.xml} (or if provided {@code talend.component.manager.m2.settings}) for checking if
043 * a property localRepository is defined</li>
044 * <li>checking for maven env properties (ie $M2_HOME)</li>
045 * <li>fallbacks to ${user.home}/.m2/repository</li>
046 * </ul>
047 */
048@Slf4j
049@NoArgsConstructor
050@Data
051public class MavenRepositoryDefaultResolver implements MavenRepositoryResolver {
052
053    private PathHandler handler = new PathHandlerImpl();
054
055    @Override
056    public Path discover() {
057        return Stream
058                .of(fromSystemProperties(), fromStudioConfiguration(), fromMavenSettings(), fromEnvironmentVariables())
059                .filter(Objects::nonNull)
060                .findFirst()
061                .orElseGet(this::fallback);
062    }
063
064    @Override
065    public Path fallback() {
066        log.debug("[fallback] default to user's default repository.");
067        return Paths.get(USER_HOME).resolve(M2_REPOSITORY);
068    }
069
070    public Path fromSystemProperties() {
071        final String m2 = System.getProperty(TALEND_COMPONENT_MANAGER_M2_REPOSITORY);
072        if (m2 != null) {
073            return handler.get(m2);
074        }
075        log.debug("[fromSystemProperties] Could not get m2 from System property.");
076        return null;
077    }
078
079    private Path fromStudioConfiguration() {
080        // check if we are in the studio process if so just grab the studio config
081        final String m2Repo = System.getProperty(STUDIO_MVN_REPOSITORY);
082        if (!"global".equals(m2Repo)) {
083            final String osgi = System.getProperty("osgi.configuration.area", "");
084            try {
085                return osgi != null && osgi.startsWith("file") ? handler.get(Paths.get(new URI(osgi)).toString())
086                        : handler.get(Paths.get(osgi, M2_REPOSITORY).toString());
087            } catch (java.net.URISyntaxException e) {
088                log.debug("[fromStudioConfiguration] Could not get m2 from studio config." + e.getMessage());
089                return null;
090            }
091        }
092        log.debug("[fromStudioConfiguration] Could not get m2 from studio config.");
093        return null;
094    }
095
096    private static String parseSettings(final Path settings) {
097        log.debug("[fromMavenSettings] searching for localRepository location in {}", settings);
098        try {
099            String localM2RepositoryFromSettings = null;
100            // do not introduce a xml parser so will do with what we have...
101            final String content = new String(java.nio.file.Files.readAllBytes(settings), StandardCharsets.UTF_8);
102            // some cleanups
103            String stripped = XML_COMMENTS_PATTERN.matcher(content).replaceAll("");
104            stripped = XML_EMPTY_LINES_PATTERN.matcher(stripped).replaceAll("");
105            // localRepository present?
106            final Matcher m = XML_LOCAL_REPO_PATTERN.matcher(stripped);
107            if (!m.matches()) {
108                log.debug("[fromMavenSettings] localRepository not defined in settings.xml");
109            } else {
110                localM2RepositoryFromSettings = m.group(1).trim();
111                if (localM2RepositoryFromSettings != null && !localM2RepositoryFromSettings.isEmpty()) {
112                    return localM2RepositoryFromSettings;
113                }
114            }
115        } catch (final Exception ignore) {
116            // fallback on default local path
117        }
118        return null;
119    }
120
121    public Path fromMavenSettings() {
122        return Stream.of(
123                Optional.ofNullable(System.getProperty(TALEND_COMPONENT_MANAGER_M2_SETTINGS))
124                        .map(Paths::get)
125                        .orElse(null), //
126                Optional.ofNullable(System.getProperty("user.home")).map(it -> Paths.get(it, M2_SETTINGS)).orElse(null),
127                Optional.ofNullable(System.getenv(MAVEN_HOME)).map(it -> Paths.get(it, CONF_SETTINGS)).orElse(null),
128                Optional.ofNullable(System.getenv(M2_HOME)).map(it -> Paths.get(it, CONF_SETTINGS)).orElse(null))
129                .filter(Objects::nonNull)
130                .filter(Files::exists)
131                .map(MavenRepositoryDefaultResolver::parseSettings)
132                .filter(Objects::nonNull)
133                .map(handler::get)
134                .filter(Objects::nonNull)
135                .findFirst()
136                .orElse(null);
137    }
138
139    public Path fromEnvironmentVariables() {
140        final String vm2 = System.getenv(M2_HOME);
141        log.debug("[fromEnvironmentVariables] M2_HOME={}", vm2);
142        if (vm2 != null) {
143            return handler.get(Paths.get(vm2, "repository").toString());
144        }
145        log.debug("[fromEnvironmentVariables] Could not get m2 from environment.");
146        return null;
147    }
148
149}