/* * Copyright 2000-2011 JetBrains s.r.o. * * 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 jetbrains.buildServer.tools.installer; import java.io.BufferedInputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Map; import jetbrains.buildServer.core.runtime.IProgressMonitor; import jetbrains.buildServer.core.runtime.osgx.IToolDescriptor; import jetbrains.buildServer.core.runtime.osgx.ToolRepository; import jetbrains.buildServer.serverSide.BuildFeature; import jetbrains.buildServer.serverSide.BuildStartContext; import jetbrains.buildServer.serverSide.BuildStartContextProcessor; import jetbrains.buildServer.serverSide.SBuildFeatureDescriptor; import jetbrains.buildServer.serverSide.SBuildServer; import jetbrains.buildServer.serverSide.SBuildType; import jetbrains.buildServer.serverSide.TeamCityProperties; import jetbrains.buildServer.tools.ToolsConstants; import jetbrains.buildServer.tools.ToolsUtil; import jetbrains.buildServer.web.openapi.PluginDescriptor; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.util.io.StreamUtil; public class ToolsInstallerFeature extends BuildFeature implements BuildStartContextProcessor { private static final Logger LOG = Logger.getLogger(ToolsInstallerFeature.class); private static final IProgressMonitor MONITOR = new ToolsUtil.LoggerProgressMonitor(LOG); private String myEditUrl; private static ToolRepository ourRepository; private static ToolsInstallerFeature ourInstance; public static ToolsInstallerFeature getInstance() { return ourInstance; } public ToolsInstallerFeature(final @NotNull SBuildServer server, @NotNull final PluginDescriptor descriptor) { myEditUrl = descriptor.getPluginResourcesPath("toolsinstallerSettings.jsp"); ourInstance = this; } static ToolRepository getToolRepository() throws IOException { if (ourRepository == null) { //load bundled final ToolRepository primaryRepo = ToolRepository.getBundleRepository(MONITOR); if (primaryRepo != null) { ourRepository = primaryRepo; LOG.debug(String.format("Bundled repository loaded")); } else { LOG.error(String.format("Bundled is not found")); } //load propertied final ToolRepository customRepo = loadCustomRepository(); if (customRepo != null) { if (ourRepository != null) { ourRepository.merge(customRepo, MONITOR); } else { ourRepository = customRepo; } } } return ourRepository; } static ToolRepository loadCustomRepository() { final String repoLocationsProp = TeamCityProperties.getProperty(ToolsConstants.TEAMCITY_TOOLS_REPOSITORY_LOCATION_URI); if (repoLocationsProp != null && repoLocationsProp.trim().length() > 0) { final String[] repoLocations = repoLocationsProp.split(";"); if (repoLocations.length > 0) { try { ToolRepository current = new ToolRepository(new URI(repoLocations[0])); for (int i = 1; i < repoLocations.length; i++) { if (repoLocations[i].trim().length() > 0) { current = current.merge(new ToolRepository(new URI(repoLocations[i])), MONITOR); } } return current; } catch (URISyntaxException e) { LOG.error(e.getMessage(), e); } } } return null; } @Override public String getDisplayName() { return "Install software"; } @Override public String getEditParametersUrl() { return myEditUrl; } @Override public String getType() { return "tool-installer"; } @Override public boolean isMultipleFeaturesPerBuildTypeAllowed() { return true; } @Override public String describeParameters(Map params) { try { final String idProp = params.get(ToolsConstants.TEAMCITY_REQUIRED_TOOL_ID_PROP_NAME); StringBuffer out = new StringBuffer(); if (idProp != null) { final IToolDescriptor[] tools = getToolRepository().findTool(idProp, null, null, null); if (tools != null && tools.length > 0 && tools[0].getDescription() != null) { out.append(String.format("Install '%s'", tools[0].getDescription())); } else { out.append(String.format("Install '%s'", idProp)); } final String versionProp = params.get(ToolsConstants.TEAMCITY_REQUIRED_TOOL_VERSION_PROP_NAME); if (versionProp != null) { out.append(String.format(" (%s)", versionProp)); } final String installLocationProp = params.get(ToolsConstants.TEAMCITY_REQUIRED_TOOL_INSTALL_LOCATION); if (installLocationProp != null && installLocationProp.trim().length() > 0) { out.append(String.format(" to '%s'", installLocationProp)); } return out.toString(); } } catch (Exception e) { LOG.error(e.getMessage(), e); } return super.describeParameters(params); } public void updateParameters(final @NotNull BuildStartContext context) { SBuildType buildType = context.getBuild().getBuildType(); if (buildType == null) { return; } try { Collection buildFeatures = buildType.getBuildFeatures(); for (SBuildFeatureDescriptor bf : buildFeatures) { if (bf.getType().equals(getType())) { final Map bfParameters = bf.getParameters(); final String requiredToolId = bfParameters.get(ToolsConstants.TEAMCITY_REQUIRED_TOOL_ID_PROP_NAME); if (requiredToolId != null) { scheduleInstallation(getToolRepository(), context, bf, requiredToolId, bfParameters.get(ToolsConstants.TEAMCITY_REQUIRED_TOOL_VERSION_PROP_NAME), bfParameters.get(ToolsConstants.TEAMCITY_REQUIRED_TOOL_INSTALL_LOCATION), MONITOR); } } } } catch (IOException e) { LOG.error(e.getMessage(), e); } } private void scheduleInstallation(final @NotNull ToolRepository repo, final @NotNull BuildStartContext context, final @NotNull SBuildFeatureDescriptor bf, final @NotNull String toolId, final @Nullable String toolVersion, final @Nullable String installLocation, final @NotNull IProgressMonitor monitor) throws IOException { String message = String.format("(%s) New tool installation request found: %s(%s)", bf.getId(), toolId, toolVersion); LOG.debug(message); final IToolDescriptor[] suitableTools = repo.findTool(toolId, null, null, toolVersion); if (suitableTools.length > 0) { message = String.format("Suitable tools found: %s", Arrays.asList(suitableTools)); LOG.debug(message); //keep toolId final String reqiredToolIdSharedPropName = String.format("%s.%s", ToolsConstants.TEAMCITY_REQUIRED_TOOL_ID_PROP_NAME, bf.getId()); context.addSharedParameter(reqiredToolIdSharedPropName, toolId); //manifest final String reqiredToolManifestSharedPropName = String.format("%s.%s", ToolsConstants.TEAMCITY_REQUIRED_TOOL_MANIFEST_PROP_NAME, bf.getId()); // final String srepo = new String(StreamUtil.loadFromStream(new BufferedInputStream(ToolsRepositoryAccess.toStream(suitableTools, monitor)))); final String srepo = new String(StreamUtil.loadFromStream(new BufferedInputStream(new ToolRepository(repo.getSource(), suitableTools).toStream()/*ToolsRepositoryAccess.toStream(suitableTools, monitor)*/))); LOG.debug(String.format("Tools manifest loaded: '%s'", srepo)); context.addSharedParameter(reqiredToolManifestSharedPropName, srepo); //version. can be null if (toolVersion != null) { final String reqiredToolVersionSharedPropName = String.format("%s.%s", ToolsConstants.TEAMCITY_REQUIRED_TOOL_VERSION_PROP_NAME, bf.getId()); context.addSharedParameter(reqiredToolVersionSharedPropName, getToolVersion(suitableTools)); } //location. can be null if (installLocation != null) { final String reqiredToolInstallLocationPropName = String.format("%s.%s", ToolsConstants.TEAMCITY_REQUIRED_TOOL_INSTALL_LOCATION, bf.getId()); context.addSharedParameter(reqiredToolInstallLocationPropName, installLocation); } } else { message = String.format("No suitable tools found for '%s'", toolId); LOG.debug(message); } } private String getToolVersion(final @NotNull IToolDescriptor[] tools) { return tools[0].getVersion(); } public static IToolDescriptor[] tools() throws IOException { IToolDescriptor[] allTools = getToolRepository().tools(MONITOR); final ArrayList out = new ArrayList(); for (IToolDescriptor tool : allTools) { if (!tool.isSystem()) { out.add(tool); } } return out.toArray(new IToolDescriptor[out.size()]); } }