All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.talend.sdk.component.tools.StudioInstaller Maven / Gradle / Ivy

/**
 * Copyright (C) 2006-2024 Talend Inc. - www.talend.com
 *
 * 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.talend.sdk.component.tools;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Stream;

import org.talend.sdk.component.dependencies.maven.Artifact;
import org.talend.sdk.component.dependencies.maven.MvnCoordinateToFileConverter;

public class StudioInstaller implements Runnable {

    private final String mainGav;

    private final File studioHome;

    private final Map artifacts;

    private final Log log;

    private final boolean enforceDeployment;

    private final File m2;

    public StudioInstaller(final String mainGav, final File studioHome, final Map artifacts,
            final Object log, final boolean enforceDeployment, final File m2) {
        this.mainGav = mainGav;
        this.studioHome = studioHome;
        this.artifacts = artifacts;
        this.m2 = m2;
        this.enforceDeployment = enforceDeployment;
        try {
            this.log = Log.class.isInstance(log) ? Log.class.cast(log) : new ReflectiveLog(log);
        } catch (final NoSuchMethodException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void run() {
        log.info("Installing development version of " + mainGav + " in " + studioHome);

        final List artifacts = this.artifacts.values().stream().map(File::getName).collect(toList());
        final String mvnMeta[] = mainGav.split(":");
        final String artifact = mvnMeta[1];
        final String version = mvnMeta[2];

        // 0. check if component can be installed.
        final File configIni = new File(studioHome, "configuration/config.ini");
        final Properties config = new Properties();
        if (configIni.exists()) {
            try (final InputStream is = new FileInputStream(configIni)) {
                config.load(is);
            } catch (final IOException e) {
                throw new IllegalStateException(e);
            }
        }
        String registry = config
                .getProperty("component.java.registry",
                        config.getProperty("talend.component.server.component.registry"));
        if (!enforceDeployment && registry != null && new File(registry).exists()) {
            final Properties components = new Properties();
            try (final Reader reader = new FileReader(registry)) {
                components.load(reader);
                if (components.containsKey(artifact)) {
                    final String installedVersion = components.getProperty(artifact).split(":")[2];
                    if (!version.equals(installedVersion)) {
                        throw new IllegalStateException("Can't deploy this component. A different version '"
                                + installedVersion
                                + "' is already installed.\nYou can enforce the deployment by using -Dtalend.component.enforceDeployment=true");
                    }
                }
            } catch (final IOException e) {
                log.error("Can't load registered component from the studio " + e.getMessage());
            }
        }

        // 1. remove staled libs from the cache (configuration/org.eclipse.osgi)
        final File osgiCache = new File(studioHome, "configuration/org.eclipse.osgi");
        if (osgiCache.isDirectory()) {
            ofNullable(osgiCache.listFiles(child -> {
                try {
                    return child.isDirectory() && Integer.parseInt(child.getName()) > 0;
                } catch (final NumberFormatException nfe) {
                    return false;
                }
            }))
                    .map(Stream::of)
                    .orElseGet(Stream::empty)
                    .map(id -> new File(id, ".cp"))
                    .filter(File::exists)
                    .flatMap(cp -> ofNullable(cp.listFiles((dir, name) -> name.endsWith(".jar")))
                            .map(Stream::of)
                            .orElseGet(Stream::empty))
                    .filter(jar -> artifacts.contains(jar.getName()))
                    .forEach(this::tryDelete);
        }

        // 2. install the runtime dependency tree (scope compile+runtime) in the studio m2 repo
        final String repoType = config.getProperty("maven.repository");
        if (!"global".equals(repoType)) {
            final MvnCoordinateToFileConverter converter = new MvnCoordinateToFileConverter();
            this.artifacts.forEach((gav, file) -> {
                final Artifact dependency = converter.toArtifact(gav);
                final File target = m2 == null || !m2.exists()
                        ? new File(studioHome, "configuration/.m2/repository/" + dependency.toPath())
                        : new File(m2, dependency.toPath());
                try {
                    if (target.exists() && !dependency.getVersion().endsWith("-SNAPSHOT")) {
                        log.info(gav + " already exists, skipping");
                        return;
                    }
                    copy(file, target);
                    log.info("Installed " + gav + " at " + target.getAbsolutePath());
                } catch (final IOException e) {
                    throw new IllegalStateException(e);
                }
            });
        } else {
            log
                    .info("Studio " + studioHome
                            + " configured to use global maven repository, skipping artifact installation");
        }

        // 3. register component adding them into the registry
        if (registry == null) {
            final File registryLocation = new File(configIni.getParentFile(), "components-registration.properties");
            registryLocation.getParentFile().mkdirs();
            registry = registryLocation.getAbsolutePath().replace('\\', '/');

            config.setProperty("component.java.registry", registry);
            try {
                final File backup = new File(configIni.getParentFile(), "backup/" + configIni.getName() + "_"
                        + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-mm-dd_HH-mm-ss")));
                log.info("Saving configuration in " + backup);
                copy(configIni, backup);
            } catch (final IOException e) {
                throw new IllegalStateException(e);
            }
            try (final Writer writer = new FileWriter(configIni)) {
                config
                        .store(writer, "File rewritten by " + getClass().getName()
                                + " utility to add component.java.registry entry");
                log.info("Updated " + configIni + " to add the component registry entry");
            } catch (final IOException e) {
                throw new IllegalStateException(e);
            }
            try {
                Files.write(registryLocation.toPath(), new byte[0], StandardOpenOption.CREATE_NEW);
            } catch (final IOException e) {
                throw new IllegalStateException(e);
            }
        }
        final Properties components = new Properties();
        try (final Reader reader = new FileReader(registry)) {
            components.load(reader);
        } catch (final IOException e) {
            throw new IllegalStateException(e);
        }

        if (!components.containsKey(artifact) || enforceDeployment) {
            components.setProperty(artifact, mainGav);
            try (final Writer writer = new FileWriter(registry)) {
                components.store(writer, "File rewritten to add " + mainGav);
                log.info("Updated " + registry + " with '" + mainGav + "'");
            } catch (final IOException e) {
                throw new IllegalStateException(e);
            }
        }
    }

    private void copy(final File source, final File target) throws IOException {
        mkdirP(target.getParentFile());
        try (final OutputStream to = new BufferedOutputStream(new FileOutputStream(target))) {
            Files.copy(source.toPath(), to);// this copy don't lock on win
        }
    }

    private void mkdirP(final File dir) {
        if (!dir.exists() && !dir.mkdirs()) {
            throw new IllegalStateException("Can't create " + dir);
        }
    }

    private void tryDelete(final File jar) {
        if (!jar.delete()) {
            log.error("Can't delete " + jar.getAbsolutePath());
        } else {
            log.info("Deleting " + jar.getAbsolutePath());
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy