/* * Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved. * * This program and the accompanying materials are made available * under the terms of the Eclipse Public License, Version 1.0, * which accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html * */ package net.rim.ejde.internal.util; import java.io.File; import java.io.InputStream; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import net.rim.ejde.internal.core.ContextManager; import net.rim.ejde.internal.ui.dialogs.NewVersionDetectionDialog; import net.rim.ejde.internal.ui.preferences.PreferenceConstants; import org.apache.log4j.Logger; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IBundleGroup; import org.eclipse.core.runtime.IBundleGroupProvider; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.internal.about.AboutBundleGroupData; import org.osgi.framework.Bundle; import org.osgi.framework.Version; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /* * This class is used to reminder user to obtain latest version * of BlackBerry Web Plug-in when latest version is available on * BlackBerry WebSite. * * @author qxu */ public class UpgradingNotification extends WorkspaceJob { private static final String JOB_NAME = "Upgrading Notification"; private static SimpleDateFormat df = new SimpleDateFormat( "MM/dd/yyyy" ); private static final String EJDE_CURRENTINFO_XML = "currentInfo.xml"; private static final String SAMPLE_CURRENTINFO_XML = "/templates/sample-currentInfo.xml"; private int _snoozeDays; private String _toolUpgradeUrl; private String _toolMessage; private boolean _snoozeBoolean; private boolean _initializedBoolean; private String _snoozeDate; private Version _currentToolVersion; private Version _latestToolVersion; private static final Logger _log = Logger.getLogger( UpgradingNotification.class ); public UpgradingNotification() { super( JOB_NAME ); } @Override public IStatus runInWorkspace( IProgressMonitor monitor ) { init(); if( _snoozeBoolean == true ) { if( checkSnoozeDate() ) { checkToolVersion(); } } else { checkToolVersion(); } if ( !_initializedBoolean ) { final Display display = Display.getDefault(); if( display != null && !display.isDisposed() ) { display.syncExec( new Runnable() { public void run() { if (!MessageDialog.openQuestion(display.getActiveShell(), Messages.UPGRADE_INITIALIZATION_TITLE, Messages.UPGRADE_INITIALIZATION_LABEL)) { _snoozeBoolean = true; _snoozeDays = 9999; } else { _snoozeBoolean = false; } _initializedBoolean = true; updateInfoFile(); } }); } } IStatus result = Status.OK_STATUS; return result; } /* * Initiating information of upgrade notification to create a XML file. */ private void init() { try { File currentInfoFile = getFile(); if( !currentInfoFile.exists() ) { Bundle bundle = ContextManager.PLUGIN.getBundle(); IPath path = new Path( SAMPLE_CURRENTINFO_XML ); InputStream is = FileLocator.openStream( bundle, path, false ); DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = docBuilder.parse( is ); XMLUtil.writeXmlFile( doc, currentInfoFile.getCanonicalPath(), "UTF-8", "no" ); getVersionNumber(); updateInfoFile(); } else { getInfo(); } } catch( Exception ex ) { _log.error( "Error generating XML file", ex ); } } /* * Getting current the version of tool and SDK */ private void getVersionNumber() { boolean internal = false; if( _currentToolVersion == null ) { _currentToolVersion = getFeatureVersion(); } if( internal ) { _currentToolVersion = handleVersion( _currentToolVersion.toString(), internal ); } } @SuppressWarnings("restriction") private Version getFeatureVersion() { // create a descriptive object for each BundleGroup IBundleGroupProvider[] providers = Platform.getBundleGroupProviders(); LinkedList< AboutBundleGroupData > groups = new LinkedList< AboutBundleGroupData >(); if( providers != null ) { for( int i = 0; i < providers.length; ++i ) { IBundleGroup[] bundleGroups = providers[ i ].getBundleGroups(); _log.debug( "PROVIDER NAME: " + providers[ i ].getName() ); for( int j = 0; j < bundleGroups.length; ++j ) { AboutBundleGroupData data = new AboutBundleGroupData( bundleGroups[ j ] ); groups.add( data ); if( data.getId().matches( Messages.EJDE_FEATURE_ID ) ) { _log.debug( "Found ejde feature: " + data.getId() + " version " + data.getVersion() ); return handleVersion( data.getVersion(), true ); } } } } return ContextManager.PLUGIN.getBundle().getVersion(); } private Version handleVersion( String bundleVersion, boolean internal ) { String[] bundleNumber = bundleVersion.split( "\\-" ); String[] bundleSeries = bundleVersion.split( "\\." ); String qualifier; int[] temp = new int[ 4 ]; for( int i = 0; i < bundleSeries.length - 1; i++ ) { temp[ i ] = Integer.parseInt( bundleSeries[ i ] ); } if( internal ) { qualifier = bundleNumber.length > 1 ? bundleNumber[ 1 ] : "0"; //current dev build do not have qualifier } else { qualifier = bundleSeries[ 3 ]; } _log.debug( "qualifier: " + qualifier ); return new Version( temp[ 0 ], temp[ 1 ], temp[ 2 ], qualifier ); } /* * Getting all version information from internal XML file about current plug-in. */ private void getInfo() { try { File currentInfo = getFile(); if( currentInfo.exists() && currentInfo.isFile() ) { Document xmlDoc = XMLUtil.openXmlFile( currentInfo, false ); if( xmlDoc != null ) { XPath xPath = XPathFactory.newInstance().newXPath(); _currentToolVersion = handleVersion( xPath.evaluate("/version/tool[1]/text()", xmlDoc), false ); _initializedBoolean = Boolean.parseBoolean( xPath.evaluate("/version/initialized[1]/text()", xmlDoc) ); _snoozeBoolean = Boolean.parseBoolean( xPath.evaluate("/version/snooze/boolean[1]/text()", xmlDoc) ); if( _snoozeBoolean ) { _snoozeDays = Integer.parseInt( xPath.evaluate("/version/snooze/days[1]/text()", xmlDoc) ); _snoozeDate = xPath.evaluate( "/version/snooze/date[1]/text()", xmlDoc); } } } } catch( Exception ex ) { _log.error( "Error loading file information", ex ); } } /* * Checking whether the snooze days has been coming. * * @return */ private Boolean checkSnoozeDate() { try { Date currentDate = new Date(); Date pastDate = df.parse( _snoozeDate ); int distance = Math.abs( (int) ( ( currentDate.getTime() - pastDate.getTime() ) / 24 / 60 / 60 / 1000 ) ); if( distance >= _snoozeDays ) { return true; } } catch( ParseException e ) { e.printStackTrace(); } return false; } /* * Checking whether current plug-in version is lower than latest version on BlackBerry WebSite or equal to latest version * * @return */ private Boolean checkToolVersion() { retrieveXMLInfo(); getVersionNumber(); if( _latestToolVersion != null ) { int result = resultFromDialog( _currentToolVersion, _latestToolVersion, _toolMessage, _toolUpgradeUrl, Messages.UPGRADE_NOTIFICATION_OF_EJDE_PLUGIN_TITLE ); switch( result ) { case 0: _snoozeBoolean = true; updateInfoFile(); break; case 1: _snoozeBoolean = false; _currentToolVersion = _latestToolVersion; updateInfoFile(); break; case 2: _snoozeBoolean = true; _snoozeDays = 9999; updateInfoFile(); break; default: return false; } return true; } else { _log.error( "Upgrade ejde Plug-In version can't be found on BlackBerry Website for some reasons." ); } return false; } /* * Retrieving all information of latest plug-in version from external XML file on BlackBerry WebSite. * * @param projectType */ private void retrieveXMLInfo() { try { // Notice: External url has to replace internal url when releasing. // this url only is for internal testing in tester team URL url; Document xmlDoc = null; String internalURL = ContextManager.PLUGIN.getPreferenceStore().getString( PreferenceConstants.UPDATE_NOTIFY_URL ); if( internalURL.isEmpty() ) { // check internal url = new URL( Messages.INTERNAL_TESTING_URL ); xmlDoc = XMLUtil.openXMLStream( url ); // if does not find in internal, try the release version if( xmlDoc == null ) { url = new URL( Messages.EXTERNAL_DEVZONE_URL ); xmlDoc = XMLUtil.openXMLStream( url ); } } else { // use for internal testing url = new URL( internalURL ); xmlDoc = XMLUtil.openXMLStream( url ); } if( xmlDoc != null ) { XPathFactory factory = XPathFactory.newInstance(); XPath xPath = factory.newXPath(); NodeList nodeToolList = (NodeList) xPath.evaluate( "/upgrade/software", xmlDoc, XPathConstants.NODESET ); for( int i = 0; i < nodeToolList.getLength(); i++ ) { Node node = nodeToolList.item( i ); if( node instanceof Element ) { String toolID = ( (Element) node ).getAttribute( Messages.TOOL_ID ); if( toolID.equals( "eJDE" ) ) { String toolVersionStr = ( (Element) node ).getAttribute( Messages.LATEST_VERSION ); _latestToolVersion = handleVersion( toolVersionStr, false ); _toolUpgradeUrl = ( (Element) node ).getAttribute( Messages.UPGRADE_URL ); Element messageElmntLst = (Element) node; NodeList messageElmnt = messageElmntLst.getElementsByTagName( "message" ); NodeList messageNm = messageElmnt.item( 0 ).getChildNodes(); _toolMessage = messageNm.item( 0 ).getNodeValue(); } } } } else { // give up: cannot find the version file from: test, internal and external _log.debug( "Cannot find the version xml file from host : " + url.getHost() ); } } catch( Exception ex ) { _log.error( "Error retrieving upgrade version info.", ex ); } } private Element constructPath(Document xmlDoc, XPath xPath, String path) throws XPathExpressionException { Element e = (Element)xPath.evaluate("/" + path + "[1]", xmlDoc, XPathConstants.NODE); if (e == null) { int i = path.lastIndexOf('/'); if (i<0) { e = xmlDoc.createElement(path); xmlDoc.getDocumentElement().appendChild(e); } else { e = xmlDoc.createElement(path.substring(i + 1)); constructPath(xmlDoc, xPath, path.substring(0, i)).appendChild(e); } } return e; } /* * Updating information in the internal XML file */ private void updateInfoFile() { try { File currentInfo = getFile(); if( currentInfo.exists() && currentInfo.isFile() ) { Document xmlDoc = XMLUtil.openXmlFile( currentInfo, false ); if( xmlDoc != null ) { XPath xPath = XPathFactory.newInstance().newXPath(); constructPath(xmlDoc, xPath, "version/tool").setTextContent( _currentToolVersion.toString() ); constructPath(xmlDoc, xPath, "version/initialized").setTextContent( Boolean.toString( _initializedBoolean ) ); constructPath(xmlDoc, xPath, "version/snooze/boolean").setTextContent( Boolean.toString( _snoozeBoolean ) ); constructPath(xmlDoc, xPath, "version/snooze/days").setTextContent( _snoozeDays + "" ); constructPath(xmlDoc, xPath, "version/snooze/date").setTextContent( df.format( new Date() ) ); XMLUtil.writeXmlFile( xmlDoc, currentInfo.getCanonicalPath(), "UTF-8", "no" ); } } } catch( Exception ex ) { _log.error( "Error updating file information", ex ); } } /* * Check whether the internal XML is available in plug-in * * @return */ private File getFile() { try { IPath location = new Path( VMToolsUtils.getVMToolsFolderPath() + File.separator + EJDE_CURRENTINFO_XML ); File currentInfoFile = location.toFile(); return currentInfoFile; } catch( Exception ex ) { _log.error( "Error generating XML file", ex ); } return null; } /* * Showing up a dialog whether users are interesting in latest version of web plug-in * * @param current version, latest version, dialog message * * @return */ private int resultFromDialog( Version currentVersion, Version webVersion, String message, String upgradeUrl, String messageTitle ) { String[] dialogButtonLabels = { Messages.SNOOZE_DAYS_BUTTON, Messages.IGNORE_UPDATE_BUTTON, Messages.IGNORE_ALL_UPDATES_BUTTON }; if( RIMVersionComparator.getInstance().compare( currentVersion, webVersion, RIMVersionComparator.VERSION_ALL ) < 0 ) { DialogOutput dialog = openMessageDialog( messageTitle, message, upgradeUrl, dialogButtonLabels ); _initializedBoolean = true; return dialog.getStatus(); } return -1; } /** * Help method for opening a dialog * * @param title * @param message * @param dialogButtonLabels * @return */ private DialogOutput openMessageDialog( final String title, final String message, final String upgradeUrl, final String[] dialogButtonLabels ) { final DialogOutput result = new DialogOutput(); final Display display = Display.getDefault(); if( display != null && !display.isDisposed() ) { display.syncExec( new Runnable() { public void run() { NewVersionDetectionDialog dialog = new NewVersionDetectionDialog( display.getActiveShell(), title, null, message, upgradeUrl, MessageDialog.INFORMATION, dialogButtonLabels, 0 ); result.setStatus( dialog.open() ); _snoozeDays = dialog.getSnoozeDays(); } } ); } return result; // return status of the dialog } /** * Stores dialog output after it has been exited * * @author hrevinskaya */ public static class DialogOutput { private int _status = Dialog.OK; private boolean _checkboxStatus = false; /** * Set return status * * @param status * return status of a dialog */ public void setStatus( int status ) { _status = status; } /** * Set status of the dialog's checkbox * * @param checkboxStatus * true if checkbox was checked; false otherwise */ public void setCheckBoxStatus( boolean checkboxStatus ) { _checkboxStatus = checkboxStatus; } /** * Get return status of the dialog * * @return status of the dialog */ public int getStatus() { return _status; } /** * Get checkbox status of the dialog * * @return true if the checkbox was checked when dialog was exited; false otherwise */ public boolean getCheckboxStatus() { return _checkboxStatus; } } }