/*****************************************************************************
* Copyright (c) 2006-2007, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
*****************************************************************************/
package org.eclipse.buckminster.jnlp.p2.bootstrap;
import static org.eclipse.buckminster.jnlp.p2.bootstrap.BootstrapConstants.ERROR_CODE_DEFAULT_BROWSER_NOT_AVAILABLE_EXCEPTION;
import static org.eclipse.buckminster.jnlp.p2.bootstrap.BootstrapConstants.ERROR_CODE_JAVA_RUNTIME_EXCEPTION;
import static org.eclipse.buckminster.jnlp.p2.bootstrap.BootstrapConstants.ERROR_CODE_REMOTE_IO_EXCEPTION;
import static org.eclipse.buckminster.jnlp.p2.bootstrap.BootstrapConstants.REPORT_ERROR_PREFIX;
import static org.eclipse.buckminster.jnlp.p2.bootstrap.BootstrapConstants.REPORT_ERROR_VIEW;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import sun.misc.BASE64Encoder;
/**
* @author Filip Hrbek
*
* Useful utilities.
*/
public class Utils
{
public static String UTF_8 = "UTF-8"; //$NON-NLS-1$
public static String NEW_LINE_SEPARATOR = "\n"; //$NON-NLS-1$
public static void close(Closeable closeable)
{
if(closeable != null)
{
try
{
closeable.close();
}
catch(IOException e)
{
}
}
}
public static String createExceptionMessage(Throwable throwable)
{
String[] systemProps = { "os.name", "os.arch", "user.country", "java.vendor", "java.version" };
StringBuilder messageBuilder = new StringBuilder();
messageBuilder.append(Messages.getString("environment_colon"));
messageBuilder.append(NEW_LINE_SEPARATOR);
for(String prop : systemProps)
{
messageBuilder.append(prop);
messageBuilder.append("=");
messageBuilder.append(System.getProperty(prop));
messageBuilder.append(NEW_LINE_SEPARATOR);
}
messageBuilder.append(NEW_LINE_SEPARATOR);
messageBuilder.append(Messages.getString("stack_trace_colon"));
messageBuilder.append(NEW_LINE_SEPARATOR);
StringWriter stackTrace = new StringWriter();
PrintWriter stackTraceWriter = null;
try
{
stackTraceWriter = new PrintWriter(stackTrace);
throwable.printStackTrace(stackTraceWriter);
}
finally
{
if(stackTraceWriter != null)
{
stackTraceWriter.flush();
stackTraceWriter.close();
}
}
messageBuilder.append(stackTrace);
return messageBuilder.toString();
}
/**
* Creates a hash code for specified url, suitable for creating a folder in the cache structure.
*
* @param jnlp
* @return
*/
public static String createHash(URL jnlp)
{
MessageDigest md;
try
{
md = MessageDigest.getInstance("MD5"); //$NON-NLS-1$
}
catch(NoSuchAlgorithmException e)
{
throw new RuntimeException(e);
}
return new BASE64Encoder().encode(md.digest(jnlp.toString().getBytes()));
}
public static Image createImage(byte[] imageData)
{
return imageData != null
? Toolkit.getDefaultToolkit().createImage(imageData)
: null;
}
public static Image createImage(String url) throws JNLPException
{
return createImage(loadData(url));
}
public static String createMailtoURL(String to, String subject, String body)
{
StringBuilder sb = new StringBuilder();
sb.append("mailto:");
sb.append(mailtoEncode(to));
sb.append("?subject=");
sb.append(mailtoEncode(subject));
sb.append("&body=");
sb.append(mailtoEncode(body));
return sb.toString();
}
public static void deleteRecursive(File file) throws JNLPException
{
if(!file.exists())
return;
try
{
File[] list = file.listFiles();
int count = (list == null)
? 0
: list.length;
if(count > 0)
{
while(--count >= 0)
deleteRecursive(list[count]);
}
if(!file.delete() && file.exists())
throw new JNLPException(
Messages.getString("unable_to_delete") + file.getAbsolutePath(), Messages.getString("check_file_permissions"), //$NON-NLS-1$ //$NON-NLS-2$
BootstrapConstants.ERROR_CODE_FILE_IO_EXCEPTION);
}
catch(SecurityException e)
{
throw new JNLPException(
Messages.getString("unable_to_delete") + file.getAbsolutePath() + ": " + e.getMessage(), //$NON-NLS-1$ //$NON-NLS-2$
Messages.getString("check_file_permissions"), BootstrapConstants.ERROR_CODE_FILE_IO_EXCEPTION, e); //$NON-NLS-1$
}
}
public static void emailException(String recipient, String subject, Throwable throwable) throws JNLPException
{
String mailtoURL = createMailtoURL(recipient, subject, createExceptionMessage(throwable));
// TODO create a more stable solution of sending emails
// Win-XP does not open link with more then 2000 chars
mailtoURL = mailtoURL.substring(0, Math.min(mailtoURL.length(), 2000));
showInBrowser(mailtoURL);
}
/**
* Formats a timestamp to a format suitable for easy cache management.
*
* @param date
* @return
*/
public static String formatDate(Date date)
{
return formatDate(Long.valueOf(date.getTime()));
}
/**
* Formats a timestamp to a format suitable for easy cache management.
*
* @param timestamp
* @return
*/
public static String formatDate(Long timestamp)
{
return String.format("%d", timestamp); //$NON-NLS-1$
}
/**
* This method prepares argument with proxy information which will be passed to the application. Notice that there
* arguments don't set system properties, they are supposed to be parsed in the application to set up the proxy
* rules internally.
*
* The algorithm of getting proxy information is not ideal since the proxy selector might use non-trivial rules.
* However, we don't know which proxy selector implementation will handle our requests and there is no way of
* retrieving all the proxy rules.
*
* Let's keep it simple - we try to use dummy addresses for the most common protocols. This will guarantee that we
* inherit most probable browser proxy settings.
*
* If the rules are not guessed optimally, there should be an option in the launched application to override
* automatic proxy discovery with user's own rules, with the possibility to persist the settings in the application
* installation directory.
*
* @return
* @throws JNLPException
* @throws URISyntaxException
*/
public static List<String> getProxySettings() throws JNLPException
{
List<String> args;
try
{
args = new ArrayList<String>();
ProxySelector proxySelector = ProxySelector.getDefault();
for(URI uri : new URI[] { new URI("http://dummy.host.com"), new URI("https://dummy.host.com"), //$NON-NLS-1$ //$NON-NLS-2$
new URI("ftp://dummy.host.com") }) //$NON-NLS-1$
{
List<Proxy> proxies = proxySelector.select(uri);
String protocol = uri.getScheme();
for(Proxy proxy : proxies)
{
if(Proxy.NO_PROXY.equals(proxy))
break;
SocketAddress address = proxy.address();
if(address instanceof InetSocketAddress)
{
InetSocketAddress iaddr = (InetSocketAddress)address;
args.add("-D" + protocol + ".proxyHost"); //$NON-NLS-1$ //$NON-NLS-2$
args.add(iaddr.getHostName());
args.add("-D" + protocol + ".proxyPort"); //$NON-NLS-1$ //$NON-NLS-2$
args.add("" + iaddr.getPort()); //$NON-NLS-1$
args.add("-D" + protocol + ".nonProxyHosts"); //$NON-NLS-1$ //$NON-NLS-2$
args.add("localhost|127.0.0.1"); //$NON-NLS-1$
break;
}
}
}
}
catch(URISyntaxException e)
{
throw new JNLPException(
Messages.getString("unable_to_detect_proxy_settings"), Messages.getString("report_the_problem"), //$NON-NLS-1$ //$NON-NLS-2$
ERROR_CODE_JAVA_RUNTIME_EXCEPTION);
}
return args;
}
public static byte[] loadData(String url) throws JNLPException
{
byte[] data = null;
if(url != null)
{
InputStream is = null;
try
{
is = new URL(url).openStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buf = new byte[0x1000];
int count;
while((count = is.read(buf)) > 0)
os.write(buf, 0, count);
data = os.toByteArray();
}
catch(IOException e)
{
throw new JNLPException(
Messages.getString("unable_to_read_a_splash_screen_or_window_icon_image"), //$NON-NLS-1$
Messages.getString("check_your_internet_connection_and_try_again"), ERROR_CODE_REMOTE_IO_EXCEPTION, e); //$NON-NLS-1$
}
finally
{
Utils.close(is);
}
}
return data;
}
public static String mailtoEncode(String input)
{
String output;
try
{
output = URLEncoder.encode(input, UTF_8);
}
catch(UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
return output.replace("+", "%20");
}
/**
* Converts a single -extra string parameter into a list of parameters. Parameters are delimited by space. Example:
* -extra "-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y"
*
* @param args
* @return
*/
public static List<String> parseExtraArgs(String extraArgsString)
{
if(extraArgsString != null && !"null".equals(extraArgsString)) //$NON-NLS-1$
{
String[] extraArgs = extraArgsString.split(" "); //$NON-NLS-1$
return Arrays.asList(extraArgs);
}
return Collections.emptyList();
}
public static void reportToServer(String basePathURL, String errorCode, Throwable throwable) throws IOException
{
if(basePathURL == null)
return;
String string = basePathURL + REPORT_ERROR_VIEW + "?errorCode=" + REPORT_ERROR_PREFIX + errorCode + //$NON-NLS-1$
"&errorMessage=" + createExceptionMessage(throwable); //$NON-NLS-1$
URL feedbackURL = new URL(string);
// ping feedback view to report it to apache log
InputStream is = feedbackURL.openStream();
byte[] copyBuf = new byte[8192];
while(is.read(copyBuf) > 0)
;
is.close();
}
/**
* Opens url in a default browser
*
* @param url
* @throws JNLPException
*/
// from Java 1.6 use java.awt.Desktop.getDesktop().browse(URI)
public static void showInBrowser(String url) throws JNLPException
{
String os = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$
Runtime rt = Runtime.getRuntime();
try
{
if(os.indexOf("win") >= 0) //$NON-NLS-1$
{
// alternative command: cmd.exe /C start <url> - however, it doesn't like '&' in url
String[] cmd = new String[3];
cmd[0] = "rundll32"; //$NON-NLS-1$
cmd[1] = "url.dll,FileProtocolHandler"; //$NON-NLS-1$
cmd[2] = url;
rt.exec(cmd);
}
else if(os.indexOf("mac") >= 0) //$NON-NLS-1$
{
rt.exec("open " + url); //$NON-NLS-1$
}
else
{
// prioritized 'guess' of users' preference
String[] browsers = { "epiphany", "firefox", "mozilla", "konqueror", "netscape", "opera", "chrome" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
StringBuffer cmd = new StringBuffer();
for(int i = 0; i < browsers.length; i++)
cmd.append((i == 0
? "" //$NON-NLS-1$
: " || ") + browsers[i] + " \"" + url + "\" "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
rt.exec(new String[] { "sh", "-c", cmd.toString() }); //$NON-NLS-1$ //$NON-NLS-2$
}
}
catch(IOException e)
{
throw new JNLPException(
Messages.getString("cannot_open_default_web_browser"), Messages.getString("your_web_browser_is_not_properly_installed"), ERROR_CODE_DEFAULT_BROWSER_NOT_AVAILABLE_EXCEPTION, e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* Copies specified input stream into the output stream and closes the input stream whil the output stream remains
* open.
*
* @param is
* @param os
* @throws IOException
*/
public static void streamCopy(InputStream is, OutputStream os) throws IOException
{
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1)
{
os.write(buffer, 0, len);
}
is.close();
}
}