/******************************************************************************* * Copyright (c) 2010, 2014 Itema AS and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Torkild U. Resheim - initial API and implementation * Torkild U. Resheim - Uniquely identify Jenkins servers, bug 341725 * Torkild U. Resheim - Distinguish between Hudson and Jenkins, bug 353861 * Tasktop Technologies - improvements *******************************************************************************/ package org.eclipse.mylyn.internal.hudson.ui; import java.net.URISyntaxException; import java.util.List; import java.util.UUID; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.ecf.core.ContainerConnectException; import org.eclipse.ecf.core.ContainerCreateException; import org.eclipse.ecf.core.ContainerFactory; import org.eclipse.ecf.core.IContainer; import org.eclipse.ecf.discovery.IDiscoveryLocator; import org.eclipse.ecf.discovery.IServiceEvent; import org.eclipse.ecf.discovery.IServiceInfo; import org.eclipse.ecf.discovery.IServiceListener; import org.eclipse.ecf.discovery.IServiceProperties; import org.eclipse.ecf.discovery.identity.IServiceID; import org.eclipse.ecf.discovery.identity.IServiceTypeID; import org.eclipse.mylyn.builds.ui.BuildsUi; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.commons.repositories.core.RepositoryLocation; import org.eclipse.osgi.util.NLS; /** * This class implements a mechanism for discovering Hudson and Jenkins servers through the use of Multicast DNS (MDNS). * * @author Torkild U. Resheim, Itema AS * @author Steffen Pingel */ public class HudsonDiscovery { /** * This class works around a source incompatibility between the org.eclipse.ecf.discovery version in Luna and * earlier versions. Version 5.0 added the triggerDiscovery method to IServiceListener. This class can be extended * in order to implement this method without the @Overide annotation causing compilation to fail against earlier * versions (e.g. Kepler). */ private static abstract class AbstractServiceListener { public abstract boolean triggerDiscovery(); } private final class HudsonServiceListener extends AbstractServiceListener implements IServiceListener { public void serviceDiscovered(IServiceEvent anEvent) { IServiceInfo serviceInfo = anEvent.getServiceInfo(); IServiceID serviceId = serviceInfo.getServiceID(); IServiceTypeID serviceTypeId = serviceId.getServiceTypeID(); // Note that Jenkins will claim that it's both Jenkins and // Hudson for backward compatibility. if (serviceTypeId.getName().equals(JENKINS_MDNS_ID)) { IServiceProperties properties = serviceInfo.getServiceProperties(); try { if (properties.getProperty(URL_PROPERTY) == null) { notifyMessage(Messages.JenkinsDiscovery_MessageTitle, NLS.bind( Messages.JenkinsDiscovery_MissingURL, new Object[] { serviceInfo.getLocation() .getHost() })); } else { issueJenkinsNotification(properties); } } catch (URISyntaxException e) { StatusHandler.log(new Status(IStatus.ERROR, HudsonConnectorUi.ID_PLUGIN, NLS.bind( Messages.Discovery_IncorrectURI, new Object[] { properties.getProperty(URL_PROPERTY) .toString() }), e)); } } if (serviceTypeId.getName().equals(HUDSON_MDNS_ID)) { IServiceProperties properties = serviceInfo.getServiceProperties(); try { if (properties.getProperty(URL_PROPERTY) == null) { notifyMessage(Messages.HudsonDiscovery_MessageTitle, NLS.bind( Messages.HudsonDiscovery_MissingURL, new Object[] { serviceInfo.getLocation().getHost() })); } else { issueHudsonNotification(properties); } } catch (URISyntaxException e) { StatusHandler.log(new Status(IStatus.ERROR, HudsonConnectorUi.ID_PLUGIN, NLS.bind( Messages.Discovery_IncorrectURI, new Object[] { properties.getProperty(URL_PROPERTY) .toString() }), e)); } } } public void serviceUndiscovered(IServiceEvent anEvent) { // Ignore this for now } @Override public boolean triggerDiscovery() { return false; } } private static final String ECF_DISCOVERY_JMDNS = "ecf.discovery.jmdns"; //$NON-NLS-1$ private static final String HUDSON_MDNS_ID = "_hudson._tcp.local._iana"; //$NON-NLS-1$ private static final String JENKINS_MDNS_ID = "_jenkins._tcp.local._iana"; //$NON-NLS-1$ private static final String URL_PROPERTY = "url"; //$NON-NLS-1$ /** Server id property name (Jenkins only). */ private static final String SERVER_ID_PROPERTY = "server-id"; //$NON-NLS-1$ private IContainer container; public HudsonDiscovery() { } protected IContainer getContainer() throws ContainerCreateException { return ContainerFactory.getDefault().createContainer(ECF_DISCOVERY_JMDNS); } /** * Determines whether or not the detected server is a new server or not. * * @param url * the server URL * @param id * the server identifier * @return <code>true</code> if the detected server is new. */ private boolean isNew(String url, String id) { if (url == null) { return false; } List<RepositoryLocation> locations = BuildsUi.getServerLocations(); for (RepositoryLocation location : locations) { if (location.hasUrl(url) || location.getId().equals(id)) { return false; } } return true; } public void start() { try { container = getContainer(); final IDiscoveryLocator adapter = (IDiscoveryLocator) container.getAdapter(IDiscoveryLocator.class); adapter.addServiceListener(new HudsonServiceListener()); container.connect(null, null); } catch (ContainerCreateException e) { StatusHandler.log(new Status(IStatus.WARNING, HudsonConnectorUi.ID_PLUGIN, Messages.Discovery_CouldNotStartService, e)); } catch (ContainerConnectException e) { StatusHandler.log(new Status(IStatus.WARNING, HudsonConnectorUi.ID_PLUGIN, Messages.Discovery_CouldNotStartService, e)); } } public void stop() { if (container != null) { container.disconnect(); container = null; } } private void notifyMessage(String title, String description) { BuildsUi.serverDiscovered(title, description); } private void issueHudsonNotification(IServiceProperties properties) throws URISyntaxException { String url = properties.getProperty(URL_PROPERTY).toString(); String id = getId(properties); if (isNew(url, id)) { notifyMessage( Messages.HudsonDiscovery_MessageTitle, NLS.bind(Messages.HudsonDiscovery_MessageText, new Object[] { url, Messages.HudsonDiscovery_ServerName, url, id })); } } private void issueJenkinsNotification(IServiceProperties properties) throws URISyntaxException { String url = properties.getProperty(URL_PROPERTY).toString(); String id = getId(properties); if (isNew(url, id)) { // Change the first segment (org.eclipse.mylyn.hudson) to the id of // the new repository type when we start differentiation between the two notifyMessage( Messages.JenkinsDiscovery_MessageTitle, NLS.bind(Messages.JenkinsDiscovery_MessageText, new Object[] { url, Messages.JenkinsDiscovery_ServerName, url, id })); } } private String getId(IServiceProperties properties) { String id = (String) properties.getProperty(SERVER_ID_PROPERTY); if (id == null) { id = UUID.randomUUID().toString(); } return id; } }