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