package hudson.plugins.tfs.model; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.microsoft.tfs.core.TFSConfigurationServer; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient; import com.microsoft.tfs.core.clients.webservices.IIdentityManagementService; import com.microsoft.tfs.core.clients.webservices.IdentityManagementException; import com.microsoft.tfs.core.clients.webservices.IdentityManagementService; import com.microsoft.tfs.core.config.persistence.DefaultPersistenceStoreProvider; import com.microsoft.tfs.core.config.persistence.PersistenceStoreProvider; import com.microsoft.tfs.core.httpclient.Credentials; import com.microsoft.tfs.core.httpclient.DefaultNTCredentials; import com.microsoft.tfs.core.httpclient.HttpClient; import com.microsoft.tfs.core.httpclient.UsernamePasswordCredentials; import com.microsoft.tfs.core.util.CredentialsUtils; import com.microsoft.tfs.core.util.URIUtils; import com.microsoft.tfs.jni.helpers.LocalHost; import com.microsoft.tfs.util.Closable; import hudson.Launcher; import hudson.ProxyConfiguration; import hudson.model.TaskListener; import hudson.plugins.tfs.TeamPluginGlobalConfig; import hudson.plugins.tfs.commands.ServerConfigurationProvider; import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.Secret; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import java.io.IOException; import java.lang.reflect.Field; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; public class Server implements ServerConfigurationProvider, Closable { private static final Logger LOGGER = Logger.getLogger(Server.class.getName()); private final String url; private final String userName; private final String userPassword; private Workspaces workspaces; private Map<String, Project> projects = new HashMap<String, Project>(); private final Launcher launcher; private final TaskListener taskListener; private final TFSTeamProjectCollection tpc; private final WebProxySettings webProxySettings; private final ExtraSettings extraSettings; private MockableVersionControlClient mockableVcc; private static HashMap<String, PersistenceStoreProvider> persistenceStoreProviderCache = new HashMap<String, PersistenceStoreProvider>(); /** * This constructor overload assumes a Jenkins instance is present. */ public Server(final Launcher launcher, final TaskListener taskListener, final String url, final String username, final String password) throws IOException { this(launcher, taskListener, url, username, password, null, null); } public static Server create(final Launcher launcher, final TaskListener taskListener, final String url, final StandardUsernamePasswordCredentials credentials, final WebProxySettings webProxySettings, final ExtraSettings extraSettings) throws IOException { final String username; final String userPassword; if (credentials == null) { username = null; userPassword = null; } else { username = credentials.getUsername(); final Secret password = credentials.getPassword(); userPassword = password.getPlainText(); } return new Server(launcher, taskListener, url, username, userPassword, webProxySettings, extraSettings); } public Server(final Launcher launcher, final TaskListener taskListener, final String url, final String username, final String password, final WebProxySettings webProxySettings, final ExtraSettings extraSettings) throws IOException { this.launcher = launcher; this.taskListener = taskListener; this.url = url; this.userName = username; this.userPassword = password; final URI uri = URIUtils.newURI(url); NativeLibraryManager.initialize(); Credentials credentials = null; // In case no user name is provided and the current platform supports // default credentials, use default credentials if ((username == null || username.length() == 0) && CredentialsUtils.supportsDefaultCredentials()) { credentials = new DefaultNTCredentials(); } else if (username != null && password != null) { credentials = new UsernamePasswordCredentials(username, password); } if (credentials != null) { final VirtualChannel channel = launcher != null ? launcher.getChannel() : null; if (webProxySettings != null) { this.webProxySettings = webProxySettings; } else { final ProxyConfiguration proxyConfiguration = determineProxyConfiguration(channel); this.webProxySettings = new WebProxySettings(proxyConfiguration); } final String host = uri.getHost(); final ProxyHostEx proxyHost = this.webProxySettings.toProxyHost(host); if (extraSettings != null) { this.extraSettings = extraSettings; } else { final TeamPluginGlobalConfig globalConfig = determineGlobalConfig(channel); this.extraSettings = new ExtraSettings(globalConfig); } final PersistenceStoreProvider defaultProvider = DefaultPersistenceStoreProvider.INSTANCE; final PersistenceStoreProvider provider; if (this.extraSettings.isConfigFolderPerNode()) { final String hostName = LocalHost.getShortName(); if(persistenceStoreProviderCache.containsKey(hostName)) { provider = persistenceStoreProviderCache.get(hostName); } else { provider = new ClonePersistenceStoreProvider(defaultProvider, hostName); persistenceStoreProviderCache.put(hostName, provider); } } else { provider = defaultProvider; } final ModernConnectionAdvisor advisor = new ModernConnectionAdvisor(proxyHost, provider); this.tpc = new TFSTeamProjectCollection(uri, credentials, advisor); } else { this.webProxySettings = null; this.extraSettings = null; this.tpc = null; } } static TeamPluginGlobalConfig determineGlobalConfig(final VirtualChannel channel) { final Jenkins jenkins = Jenkins.getInstance(); final TeamPluginGlobalConfig result; if (jenkins == null) { if (channel != null) { try { result = channel.call(new MasterToSlaveCallable<TeamPluginGlobalConfig, Throwable>() { @Override public TeamPluginGlobalConfig call() throws Throwable { final Jenkins jenkins = Jenkins.getInstance(); final TeamPluginGlobalConfig result = jenkins != null ? TeamPluginGlobalConfig.get() : null; return result; } }); } catch (final Throwable throwable) { throw new Error(throwable); } } else { result = TeamPluginGlobalConfig.DEFAULT_CONFIG; } } else { result = TeamPluginGlobalConfig.get(); } return result; } static ProxyConfiguration determineProxyConfiguration(final VirtualChannel channel) { final Jenkins jenkins = Jenkins.getInstance(); final ProxyConfiguration proxyConfiguration; if (jenkins == null) { if (channel != null) { try { proxyConfiguration = channel.call(new MasterToSlaveCallable<ProxyConfiguration, Throwable>() { public ProxyConfiguration call() throws Throwable { final Jenkins jenkins = Jenkins.getInstance(); final ProxyConfiguration result = jenkins != null ? jenkins.proxy : null; return result; } }); } catch (final Throwable throwable) { throw new Error(throwable); } } else { proxyConfiguration = null; } } else { proxyConfiguration = jenkins.proxy; } return proxyConfiguration; } public Project getProject(String projectPath) { if (! projects.containsKey(projectPath)) { projects.put(projectPath, new Project(this, projectPath)); } return projects.get(projectPath); } public Workspaces getWorkspaces() { if (workspaces == null) { workspaces = new Workspaces(this); } return workspaces; } public MockableVersionControlClient getVersionControlClient() { if (mockableVcc == null) { synchronized (this) { if (mockableVcc == null) { final VersionControlClient vcc = tpc.getVersionControlClient(); mockableVcc = new MockableVersionControlClient(vcc); } } } return mockableVcc; } public HttpClient getHttpClient() { return tpc.getHTTPClient(); } public <T, E extends Exception> T execute(final Callable<T, E> callable) { try { final VirtualChannel channel = launcher.getChannel(); final T result = channel.call(callable); return result; } catch (final Exception e) { // convert from checked to unchecked exception throw new RuntimeException(e); } } public String getUrl() { return url; } public String getUserName() { return userName; } public String getUserPassword() { return userPassword; } public Launcher getLauncher() { return launcher; } public WebProxySettings getWebProxySettings() { return webProxySettings; } public ExtraSettings getExtraSettings() { return extraSettings; } public TaskListener getListener() { return taskListener; } public synchronized void close() { if (this.mockableVcc != null) { this.mockableVcc.close(); } if (this.tpc != null) { // Close the configuration server connection that should be closed by // TFSTeamProjectCollection // The field is private, so use reflection // This should be removed when the TFS SDK is fixed // Post in MSDN forum: social.msdn.microsoft.com/Forums/vstudio/en-US/79985ef1-b35d-4fc5-af0b-b95e28402b83 try { Field f = TFSTeamProjectCollection.class.getDeclaredField("configurationServer"); f.setAccessible(true); TFSConfigurationServer configurationServer = (TFSConfigurationServer) f.get(this.tpc); if (configurationServer != null) { configurationServer.close(); } f.setAccessible(false); } catch (NoSuchFieldException ignore) { } catch (IllegalAccessException ignore) { } this.tpc.close(); } } public IIdentityManagementService createIdentityManagementService() { IIdentityManagementService ims; try { ims = new IdentityManagementService(tpc); } catch (IdentityManagementException e) { ims = new LegacyIdentityManagementService(); } return ims; } }