package rocks.inspectit.ui.rcp.job; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWebBrowser; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.FormText; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import rocks.inspectit.shared.all.version.InvalidVersionException; import rocks.inspectit.shared.all.version.Version; import rocks.inspectit.shared.all.version.VersionRelease; import rocks.inspectit.shared.all.version.VersionService; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.preferences.PreferencesConstants; import rocks.inspectit.ui.rcp.preferences.PreferencesUtils; /** * Job for checking if a new version of inspectIT exists on the GitHub. * * @author Ivan Senic * */ public class CheckNewVersionJob extends Job { /** * URI of the GitHub API GET method for the releases. */ private static final String GITHUB_RELEASES_API = "https://api.github.com/repos/inspectIT/inspectIT/releases"; /** * If job has been triggered by the user. If so the job will be performed no matter what. * Otherwise we would check first if preference for auto-check is set to <code>true</code> and * only then execute job. */ private boolean userTriggered; /** * Default constructor. * * @param userTriggered * If job has been triggered by the user. If so the job will be performed no matter * what. Otherwise we would check first if preference for auto-check is set to * <code>true</code> and only then execute job. */ public CheckNewVersionJob(boolean userTriggered) { super("Checking for new inspectIT version"); this.userTriggered = userTriggered; } /** * {@inheritDoc} */ @Override protected IStatus run(IProgressMonitor monitor) { final boolean isAutoCheck = PreferencesUtils.getBooleanValue(PreferencesConstants.AUTO_CHECK_NEW_VERSION); if (!userTriggered && !isAutoCheck) { // check for the preference and don't run if it's disabled return Status.OK_STATUS; } VersionService versionService = InspectIT.getService(VersionService.class); Version currentVersion; try { currentVersion = versionService.getVersion(); } catch (InvalidVersionException e) { return new Status(IStatus.ERROR, InspectIT.ID, "Can not read the inspectIT version.", e); } VersionRelease highestVersionRelease = null; // check on the github if there is a higher version available try { HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(GITHUB_RELEASES_API); HttpResponse response = httpClient.execute(httpGet); HttpEntity entity = response.getEntity(); try (InputStream inputStream = entity.getContent()) { Gson gson = new GsonBuilder().create(); JsonElement readed = gson.fromJson(new InputStreamReader(inputStream), JsonElement.class); if (!(readed instanceof JsonArray)) { // sometimes we are not getting JsonArray from the GitHub return new Status(userTriggered ? IStatus.ERROR : IStatus.WARNING, InspectIT.ID, "Check new version failed due to the invalid API response. Try again later. Received object: " + readed); } highestVersionRelease = getHighestVersionFromJson((JsonArray) readed); } } catch (IOException | InvalidVersionException exception) { // give feedback if user triggered it return new Status(userTriggered ? IStatus.ERROR : IStatus.WARNING, InspectIT.ID, "Error occurred reading the existing versions from GitHub during check for new version job.", exception); } if (highestVersionRelease.getVersion().compareTo(currentVersion) > 0) { // we have a new version available // display dialog in UI thread final VersionRelease highestVersionFinal = highestVersionRelease; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { NewVersionDialog dialog = new NewVersionDialog(highestVersionFinal, Display.getCurrent().getActiveShell(), isAutoCheck); dialog.open(); Boolean toggleState = dialog.getToggleState(); PreferencesUtils.saveBooleanValue(PreferencesConstants.AUTO_CHECK_NEW_VERSION, toggleState.booleanValue(), false); } }); } else if (userTriggered) { // if the user triggered the check we give him feedback that no newer version of // inspectIT is available // note that we are not in UI thread final Version currentVersionFinal = currentVersion; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { MessageDialog.openInformation(Display.getCurrent().getActiveShell(), "Check for new Version", "There is no newer version of inspectIT available. Currently running version " + currentVersionFinal.toString() + " is the latest officially released version."); } }); } return Status.OK_STATUS; } /** * Returns the highest available version release from the github JsonArray. * * @param jsonArray * JsonArray of releases. * @return Returns the highest available version release or 0.0.0 (empty release) if no release * reported in the json. * @throws InvalidVersionException * If Json contains version tags that will cause the {@link InvalidVersionException} * . */ private VersionRelease getHighestVersionFromJson(JsonArray jsonArray) throws InvalidVersionException { Version highest = new Version(0, 0, 0); VersionRelease versionRelease = new VersionRelease(highest); for (int i = 0, size = jsonArray.size(); i < size; i++) { JsonObject element = (JsonObject) jsonArray.get(i); String versionTag = element.get("tag_name").getAsString(); Version version = Version.verifyAndCreate(versionTag); if (version.compareTo(highest) > 0) { highest = version; versionRelease = new VersionRelease(version); // set pre-release and download link boolean preRelease = element.get("prerelease").getAsBoolean(); versionRelease.setPreRelease(preRelease); String htmlLink = element.get("html_url").getAsString(); versionRelease.setLink(htmlLink); } } return versionRelease; } /** * A bit changed {@link MessageDialogWithToggle} for displaying our message with link. * * @author Ivan Senic * */ private static class NewVersionDialog extends MessageDialogWithToggle { /** * Version to display in dialog. */ private VersionRelease versionRelease; /** * @param versionRelease * Version release to display in dialog * @param parentShell * the parent shell * @param toggleState * the initial state for the toggle * */ public NewVersionDialog(VersionRelease versionRelease, Shell parentShell, boolean toggleState) { super(parentShell, "Check for New Version", null, "", MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0, "Enable auto check for the new version on startup", toggleState); this.setShellStyle(this.getShellStyle() | SWT.SHEET); this.versionRelease = versionRelease; } /** * {@inheritDoc} */ @Override protected Control createMessageArea(Composite composite) { // image copied from super Image image = getImage(); if (image != null) { imageLabel = new Label(composite, SWT.NULL); image.setBackground(imageLabel.getBackground()); imageLabel.setImage(image); GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.BEGINNING).applyTo(imageLabel); } // message implementation we provide on our own if (versionRelease != null) { String type = versionRelease.isPreRelease() ? "preview" : "stable"; String message = "There is a newer version of inspectIT available. The version " + versionRelease.getVersion().toString() + " is the latest officially released " + type + " version and can be downloaded from the GitHub: " + versionRelease.getLink(); FormText messageFormText = new FormText(composite, SWT.NO_FOCUS); messageFormText.setText(message, false, true); messageFormText.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent e) { IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport(); try { IWebBrowser browser = browserSupport.createBrowser(null); URL url = new URL((String) e.getHref()); browser.openURL(url); } catch (Exception exception) { InspectIT.getDefault().createErrorDialog(exception.getMessage(), exception, -1); } } }); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).grab(true, false).hint(convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH), SWT.DEFAULT) .applyTo(messageFormText); } return composite; } } }