/*
* $Id$
*
* Copyright 2007-2013 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.system;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import ome.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contacts a given URL which should be an OME server which will return either
* an empty String or a URL which points to a needed upgrade.
*
* @author Josh Moore, josh at glencoesoftware.com
* @since 3.0-Beta2.3
*/
public class UpgradeCheck implements Runnable {
private final static Logger log = LoggerFactory.getLogger(UpgradeCheck.class);
/**
* Default timeout is 10 seconds.
*/
public final static int DEFAULT_TIMEOUT = 10 * 1000;
private static final HostnameVerifier ACCEPT_ANY_HOSTNAME = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}};
private static final SSLSocketFactory TRUSTING_SSL_SOCKET_FACTORY;
static {
final TrustManager trustEverything = new X509TrustManager() {
private final X509Certificate[] acceptedIssuers = new X509Certificate[0];
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) { }
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) { }
@Override
public X509Certificate[] getAcceptedIssuers() {
return this.acceptedIssuers;
}
};
SSLContext trustingContext = null;
try {
trustingContext = SSLContext.getInstance("SSL");
} catch (NoSuchAlgorithmException e) {
// http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
}
try {
trustingContext.init(null, new TrustManager[]{trustEverything}, null);
} catch (KeyManagementException e) {
// uses standard key manager
}
TRUSTING_SSL_SOCKET_FACTORY = trustingContext.getSocketFactory();
}
final String url;
final String version;
final int timeout;
final String agent;
String upgradeUrl = null;
Exception exc = null;
/**
* Calls {@link UpgradeCheck#UpgradeCheck(String, String, String, int)}
* using {@link #DEFAULT_TIMEOUT}
*/
public UpgradeCheck(String url, String version, String agent) {
this(url, version, agent, DEFAULT_TIMEOUT);
}
/**
* Main constructor.
*
* @param url
* Null or empty value disables check.
* @param version
* Current version as specified in the omero.properties file
* under the "omero.version" property. This can be accessed via
* <code>IConfig.getVersion() // 4.0.0</code>
* or
* <code>IConfig.getConfigValue("omero.version") // [optional-]4.0.0[-optional]</code>
* @param agent
* Name of the agent which is accessing the registry. This will
* be appended to "OMERO." in order to adhere to the registry
* API.
* @param timeout
* How long to wait for a
*/
public UpgradeCheck(String url, String version, String agent, int timeout) {
this.url = url;
this.version = version;
this.agent = "OMERO." + agent;
this.timeout = timeout;
}
public boolean isUpgradeNeeded() {
return upgradeUrl != null;
}
public String getUpgradeUrl() {
return upgradeUrl;
}
public boolean isExceptionThrown() {
return exc != null;
}
public Exception getExceptionThrown() {
return exc;
}
private void set(String results, Exception e) {
this.upgradeUrl = results;
this.exc = e;
}
/**
* If the {@link #url} has been set to null or the empty string, then no
* upgrade check will be performed (silently). If however the string is an
* invalid URL, a warning will be printed.
*
* This method should <em>never</em> throw an exception.
*/
public void run() {
// If null or empty, the upgrade check is disabled.
if (url == null || url.length() == 0) {
return; // EARLY EXIT!
}
StringBuilder query = new StringBuilder();
try {
query.append(url);
query.append("?version=");
query.append(URLEncoder.encode(version, "UTF-8"));
query.append(";os.name=");
query.append(URLEncoder.encode(System.getProperty("os.name"),
"UTF-8"));
query.append(";os.arch=");
query.append(URLEncoder.encode(System.getProperty("os.arch"),
"UTF-8"));
query.append(";os.version=");
query.append(URLEncoder.encode(System.getProperty("os.version"),
"UTF-8"));
query.append(";java.runtime.version=");
query.append(URLEncoder.encode(System
.getProperty("java.runtime.version"), "UTF-8"));
query.append(";java.vm.vendor=");
query.append(URLEncoder.encode(
System.getProperty("java.vm.vendor"), "UTF-8"));
} catch (UnsupportedEncodingException uee) {
// Internal issue
set(null, uee);
return;
}
URL _url;
try {
_url = new URL(query.toString());
} catch (Exception e) {
set(null, e);
log.error("Invalid URL: " + query.toString());
return;
}
BufferedInputStream bufIn = null;
try {
URLConnection conn = _url.openConnection();
conn.setUseCaches(false);
conn.addRequestProperty("User-Agent", agent);
conn.setConnectTimeout(timeout);
conn.setReadTimeout(timeout);
if (conn instanceof HttpsURLConnection) {
final HttpsURLConnection httpsConnection = (HttpsURLConnection) conn;
httpsConnection.setHostnameVerifier(ACCEPT_ANY_HOSTNAME);
httpsConnection.setSSLSocketFactory(TRUSTING_SSL_SOCKET_FACTORY);
}
conn.connect();
log.debug("Attempting to connect to " + query);
InputStream in = conn.getInputStream();
bufIn = new BufferedInputStream(in);
StringBuilder sb = new StringBuilder();
while (true) {
int data = bufIn.read();
if (data == -1) {
break;
} else {
sb.append((char) data);
}
}
String result = sb.toString();
if (result.length() == 0) {
log.info("no update needed");
set(null, null);
} else {
log.warn("UPGRADE AVAILABLE:" + result);
set(result, null);
}
} catch (UnknownHostException uhe) {
log.error("Unknown host:" + url);
set(null, uhe);
} catch (IOException ioe) {
log.error(String.format("Error reading from url: %s \"%s\"", query,
ioe.getMessage()));
set(null, ioe);
} catch (Exception ex) {
log.error("Unknown exception thrown on UpgradeCheck", ex);
set(null, ex);
} finally {
Utils.closeQuietly(bufIn);
}
}
}