/******************************************************************************* * Copyright (c) 2012 - 2013 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.core.preferences; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Properties; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubProgressMonitor; import org.osgi.framework.Bundle; import org.osgi.framework.Version; import org.springsource.ide.eclipse.commons.core.HttpUtil; import org.springsource.ide.eclipse.commons.internal.core.CorePlugin; import org.springsource.ide.eclipse.commons.internal.core.net.HttpClientTransportService; /** * An instance of this class provides a mechanism to retrieve String properties. * This is similar to a Java system properties. However it has support for reading these * properties from a fixed url. This allows us to change the properties after release. * <p> * Properties in this class can come from 3 different sources, listed here in * decreasing order of priority: * * 1) Java System properties (set via -Dmy.prop.name=value) in STS.ini * (properties set this way override anything else). * 2) loaded from fixed url * 3) default values hard-coded in this class. * (used only if property was not set via either 1 or 2). * * @since 3.4.M1 * * @author Kris De Volder */ public class StsProperties { public static class AscendingPriority implements Comparator<FromUrl> { public int compare(FromUrl o1, FromUrl o2) { return o1.priority - o2.priority; } } private static class FromUrl { public final String url; public final int priority; public FromUrl(IConfigurationElement element) { this.url = element.getAttribute("url"); this.priority = Integer.parseInt(element.getAttribute("priority")); } @Override public String toString() { return "("+priority+", "+url+")"; } } private static final String EXTENSION_ID = "org.springsource.ide.commons.core.properties"; private FromUrl[] readExtensionPoints() { IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtensionPoint extensionPoint = registry .getExtensionPoint(EXTENSION_ID); IExtension[] extensions = extensionPoint.getExtensions(); ArrayList<FromUrl> sources = new ArrayList<FromUrl>(); // read property definitions for (IExtension extension : extensions) { IConfigurationElement[] elements = extension .getConfigurationElements(); for (IConfigurationElement element : elements) { String name = element.getName(); if ("fromUrl".equals(name)) { sources.add(new FromUrl(element)); } } } FromUrl[] sourcesArray = sources.toArray(new FromUrl[sources.size()]); Arrays.sort(sourcesArray, new AscendingPriority()); return sourcesArray; } private static final String PROPERTIES_URL_PROPERTY = "sts.properties.url"; //Note: there is also a class called 'ResourceProvider'.. which reads various properties // from eclipse extension points. This is different because the STSProperties themselves // are read from an external url. //The ResourceProvider only allows properties to defined by extensions contained in plugins // installed into the Ecliple platform. /** * This class is a singleton. This holds the instance once created. */ private static StsProperties instance = null; public static StsProperties getInstance(IProgressMonitor mon) { if (instance==null) { StsProperties newInstance = new StsProperties(mon); instance = newInstance; } return instance; } private final Properties props; private static Boolean isHangingBug = null; private StsProperties(IProgressMonitor mon) { props = createProperties(); FromUrl[] sources = readExtensionPoints(); mon.beginTask("Read Sts Properties", sources.length+1); try { for (FromUrl source : sources) { readProperties(source.url, new SubProgressMonitor(mon, 1)); } String url = System.getProperty(PROPERTIES_URL_PROPERTY); if (url!=null) { readProperties(url, new SubProgressMonitor(mon, 1)); } } finally { mon.done(); } } /** * Determinese whether this instance is affected by the 'hanging on startup' bug: * Bug on Eclipse 3.7: https://issuetracker.springsource.com/browse/STS-3581 */ private boolean isHangingBug() { if (isHangingBug==null) { boolean affected = false; try { Bundle platformBundle = Platform.getBundle("org.eclipse.platform"); // System.err.println("org.eclipse.platform bundle: " + platformBundle); Version version = platformBundle.getVersion(); affected = version.getMajor()==3; //Both eclipse 3.7 and 3.8 are affected but not Eclipse 4.2 and 4.3 } catch (Throwable e) { CorePlugin.log(e); } finally { isHangingBug = affected; } } return isHangingBug; } private void readProperties(String url, IProgressMonitor mon) { if (url!=null) { try { InputStream content = uriStream(new URI(url), mon); if (content != null) { try { props.load(content); } finally { content.close(); } } } catch (Throwable e) { CorePlugin.log(e); //Catch and log all exceptions. This should never fail to initialise *something* usable. CorePlugin.warn("Couldn't read sts properties from '"+url+"' internal default values will be used"); } } } private InputStream uriStream(URI uri, IProgressMonitor mon) throws CoreException, MalformedURLException, IOException { if (isHangingBug()) { //Bug: using HttpUtil causes a hang. So instead we use a simple URLConnection here. return new HttpClientTransportService().stream(uri, mon); } else { try { return HttpUtil.stream(uri, mon); } catch (Exception e) { //Bug: on e45 when called 'too early' this throws an NPE. // So instead we use a simple URLConnection here. return new HttpClientTransportService().stream(uri, mon); } } } protected Properties createProperties() { Properties props = new Properties(); // Default properties (guarantees certain properties have a value no // matter what). props.put("spring.site.url", "http://spring.io"); props.put("spring.initializr.form.url", "http://start.spring.io/sts"); //Legacy no longer used as of version 3.6.3 props.put("spring.initializr.download.url", "http://start.spring.io/starter.zip"); //note: 'spring.initializr.download.url' is no longer used since STS 3.6.0. Instead // the download url is obtained by parsing the form at spring.initializr.form.url props.put("spring.initializr.json.url", "http://start.spring.io"); //Urls used in the dashboard. For each XXX.url=... property, if // - XXX.url.label is defined that label will be used for the corresponding // dashboard tab instead of the html page title (title tends to be too long). // - XXX.url.external is defined that url will always be openened in an external browser. //Switch to enable new dash (This switch no longer works as of STS 3.6.1 and is just set // statically by IIdeUiConstants.PREF_USE_OLD_DASHOARD //props.put("sts.new.dashboard.enabled", "true"); //Forum: props.put("sts.forum.url", "http://forum.springsource.org/forumdisplay.php?32-SpringSource-Tool-Suite"); props.put("sts.forum.url.label", "Forum"); props.put("sts.forum.url.external", "true"); //Tracker: props.put("sts.tracker.url", "https://issuetracker.springsource.com/browse/STS"); props.put("sts.tracker.url.label", "Issues"); props.put("sts.tracker.url.external", "true"); //Docs props.put("spring.docs.url", "https://spring.io/docs"); props.put("spring.docs.url.label", "Spring Docs"); props.put("spring.docs.url.external", "true"); //Blog props.put("spring.blog.url", "https://spring.io/blog"); props.put("spring.blog.url.label", "Blog"); props.put("spring.blog.url.external", "true"); //Guides props.put("spring.guides.url", "https://spring.io/guides"); props.put("spring.guides.url.label", "Guides"); props.put("spring.guides.url.external", "true"); //future value: "${spring.site.url}/guides" //New and Noteworthy props.put("sts.nan.url", "http://docs.spring.io/sts/nan/latest/NewAndNoteworthy.html"); //props.put("sts.nan.url.external", "true"); //Spring boot runtime props.put("spring.boot.install.url", "http://repo.spring.io/release/org/springframework/boot/spring-boot-cli/1.3.1.RELEASE/spring-boot-cli-1.3.1.RELEASE-bin.zip"); //Discovery url for spring reference app props.put("spring.reference.app.discovery.url", "https://raw.github.com/kdvolder/spring-reference-apps-meta/master/reference-apps.json"); //Url for webservice that generates typegraph for spring boot jar type content assist props.put("spring.boot.typegraph.url", "http://aetherial.cfapps.io/boot/typegraph"); //Default version of spring boot, assumed when we need a version but can't determine it from the classpath of the project. //Typically this should point to the latest version of spring-boot (the one used by spring-initialzr app). props.put("spring.boot.default.version", "1.3.6.RELEASE"); return props; } /** * Procudes names of properties that have explicitly been set, either from properties file * or by the explicitly provided defaults. More precisely this does not return * properties simply inherited from Java system properties. */ public Collection<String> getExplicitProperties() { ArrayList<String> keys = new ArrayList<String>(); for (Object string : props.keySet()) { if (string instanceof String) { keys.add((String) string); } } return keys; } public String get(String key) { String value = System.getProperty(key); if (value == null) { value = props.getProperty(key); } return value; } public boolean get(String key, boolean deflt) { String value = get(key); if (value!=null) { return Boolean.valueOf(value); } return deflt; } public static StsProperties getInstance() { return getInstance(new NullProgressMonitor()); } public URL url(String prop) { String s = get(prop); try { if (s!=null) { return new URL(get(prop)); } } catch (Exception e) { CorePlugin.log(e); } return null; } }