| /******************************************************************************* |
| * Copyright (c) 2010, 2013 The Eclipse Foundation and others. |
| * 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: |
| * The Eclipse Foundation - initial API and implementation |
| * Yatta Solutions - news (bug 401721), public API (bug 432803) |
| * JBoss (Pascal Rapicault) - Bug 406907 - Add p2 remediation page to MPC install flow |
| *******************************************************************************/ |
| package org.eclipse.epp.internal.mpc.ui.wizards; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.net.URLDecoder; |
| import java.net.URLEncoder; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.ExecutionEvent; |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.epp.internal.mpc.core.MarketplaceClientCore; |
| import org.eclipse.epp.internal.mpc.core.model.News; |
| import org.eclipse.epp.internal.mpc.ui.CatalogRegistry; |
| import org.eclipse.epp.internal.mpc.ui.MarketplaceClientUi; |
| import org.eclipse.epp.internal.mpc.ui.MarketplaceClientUiPlugin; |
| import org.eclipse.epp.internal.mpc.ui.catalog.MarketplaceCatalog; |
| import org.eclipse.epp.internal.mpc.ui.catalog.MarketplaceDiscoveryStrategy; |
| import org.eclipse.epp.internal.mpc.ui.commands.MarketplaceWizardCommand; |
| import org.eclipse.epp.internal.mpc.ui.operations.AbstractProvisioningOperation; |
| import org.eclipse.epp.internal.mpc.ui.operations.ProfileChangeOperationComputer; |
| import org.eclipse.epp.internal.mpc.ui.operations.ProfileChangeOperationComputer.OperationType; |
| import org.eclipse.epp.internal.mpc.ui.wizards.MarketplaceViewer.ContentType; |
| import org.eclipse.epp.internal.mpc.ui.wizards.SelectionModel.CatalogItemEntry; |
| import org.eclipse.epp.internal.mpc.ui.wizards.SelectionModel.FeatureEntry; |
| import org.eclipse.epp.mpc.core.model.ICategory; |
| import org.eclipse.epp.mpc.core.model.IMarket; |
| import org.eclipse.epp.mpc.core.model.INews; |
| import org.eclipse.epp.mpc.core.model.INode; |
| import org.eclipse.epp.mpc.ui.CatalogDescriptor; |
| import org.eclipse.epp.mpc.ui.MarketplaceUrlHandler; |
| import org.eclipse.epp.mpc.ui.Operation; |
| import org.eclipse.equinox.internal.p2.discovery.AbstractDiscoveryStrategy; |
| import org.eclipse.equinox.internal.p2.discovery.model.CatalogItem; |
| import org.eclipse.equinox.internal.p2.ui.ProvUI; |
| import org.eclipse.equinox.internal.p2.ui.discovery.util.WorkbenchUtil; |
| import org.eclipse.equinox.internal.p2.ui.discovery.wizards.DiscoveryWizard; |
| import org.eclipse.equinox.p2.metadata.IInstallableUnit; |
| import org.eclipse.equinox.p2.operations.ProfileChangeOperation; |
| import org.eclipse.equinox.p2.operations.ProvisioningJob; |
| import org.eclipse.equinox.p2.operations.RemediationOperation; |
| import org.eclipse.equinox.p2.operations.UninstallOperation; |
| import org.eclipse.equinox.p2.ui.AcceptLicensesWizardPage; |
| import org.eclipse.equinox.p2.ui.ProvisioningUI; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.viewers.IStructuredContentProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.wizard.IWizardContainer; |
| import org.eclipse.jface.wizard.IWizardPage; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.browser.Browser; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.browser.IWebBrowser; |
| import org.eclipse.ui.browser.IWorkbenchBrowserSupport; |
| import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| /** |
| * A wizard for interacting with a marketplace service. |
| * |
| * @author David Green |
| * @author Carsten Reckord |
| */ |
| public class MarketplaceWizard extends DiscoveryWizard implements InstallProfile, IMarketplaceWebBrowser { |
| |
| private static final String PREF_DEFAULT_CATALOG = CatalogDescriptor.class.getSimpleName(); |
| |
| private static final String DEBUG_NEWS_FLAG = MarketplaceClientUi.BUNDLE_ID + "/news/debug"; //$NON-NLS-1$ |
| |
| private static final String DEBUG_NEWS_TITLE = MarketplaceClientUi.BUNDLE_ID + "/news/title"; //$NON-NLS-1$ |
| |
| private static final String DEBUG_NEWS_URL = MarketplaceClientUi.BUNDLE_ID + "/news/url"; //$NON-NLS-1$ |
| |
| public static class WizardState { |
| private ContentType contentType; |
| |
| private Set<INode> content; |
| |
| private IMarket filterMarket; |
| |
| private ICategory filterCategory; |
| |
| private String filterQuery; |
| |
| private Boolean proceedWithInstallation; |
| |
| public ContentType getContentType() { |
| return contentType; |
| } |
| |
| public void setContentType(ContentType contentType) { |
| this.contentType = contentType; |
| } |
| |
| public Set<INode> getContent() { |
| return content; |
| } |
| |
| public void setContent(Set<INode> content) { |
| this.content = content; |
| } |
| |
| public IMarket getFilterMarket() { |
| return filterMarket; |
| } |
| |
| public void setFilterMarket(IMarket filterMarket) { |
| this.filterMarket = filterMarket; |
| } |
| |
| public ICategory getFilterCategory() { |
| return filterCategory; |
| } |
| |
| public void setFilterCategory(ICategory filterCategory) { |
| this.filterCategory = filterCategory; |
| } |
| |
| public String getFilterQuery() { |
| return filterQuery; |
| } |
| |
| public void setFilterQuery(String filterQuery) { |
| this.filterQuery = filterQuery; |
| } |
| |
| public Boolean getProceedWithInstallation() { |
| return proceedWithInstallation; |
| } |
| |
| public void setProceedWithInstallation(Boolean proceedWithInstallation) { |
| this.proceedWithInstallation = proceedWithInstallation; |
| } |
| } |
| |
| private Set<String> installedFeatures; |
| |
| private SelectionModel selectionModel; |
| |
| private MarketplaceBrowserIntegration browserListener; |
| |
| private ProfileChangeOperation profileChangeOperation; |
| |
| private FeatureSelectionWizardPage featureSelectionWizardPage; |
| |
| private AcceptLicensesWizardPage acceptLicensesPage; |
| |
| private IInstallableUnit[] operationIUs; |
| |
| private Set<CatalogItem> operationNewInstallItems; |
| |
| private boolean initialSelectionInitialized; |
| |
| private Set<URI> addedRepositoryLocations; |
| |
| private boolean withRemediation; |
| |
| private String errorMessage; |
| |
| private WizardState initialState; |
| |
| public String getErrorMessage() { |
| return errorMessage; |
| } |
| |
| public MarketplaceWizard(MarketplaceCatalog catalog, MarketplaceCatalogConfiguration configuration) { |
| super(catalog, configuration); |
| setWindowTitle(Messages.MarketplaceWizard_eclipseSolutionCatalogs); |
| createSelectionModel(); |
| withRemediation = true; |
| String withRemediationOverride = System.getProperty("marketplace.remediation.enabled"); //$NON-NLS-1$ |
| if (withRemediationOverride != null) { |
| withRemediation = Boolean.parseBoolean(withRemediationOverride); |
| } |
| } |
| |
| private void createSelectionModel() { |
| selectionModel = new SelectionModel(this) { |
| @Override |
| public void selectionChanged() { |
| super.selectionChanged(); |
| profileChangeOperation = null; |
| } |
| }; |
| } |
| |
| @Override |
| public MarketplaceCatalogConfiguration getConfiguration() { |
| return (MarketplaceCatalogConfiguration) super.getConfiguration(); |
| } |
| |
| @Override |
| public MarketplaceCatalog getCatalog() { |
| return (MarketplaceCatalog) super.getCatalog(); |
| } |
| |
| @Override |
| protected MarketplacePage doCreateCatalogPage() { |
| return new MarketplacePage(getCatalog(), getConfiguration()); |
| } |
| |
| public ProfileChangeOperation getProfileChangeOperation() { |
| return profileChangeOperation; |
| } |
| |
| public void setProfileChangeOperation(ProfileChangeOperation profileChangeOperation) { |
| this.profileChangeOperation = profileChangeOperation; |
| } |
| |
| void initializeInitialSelection() throws CoreException { |
| if (!wantInitializeInitialSelection()) { |
| throw new IllegalStateException(); |
| } |
| initialSelectionInitialized = true; |
| initializeCatalog(); |
| if (getConfiguration().getInitialState() != null || getConfiguration().getInitialOperations() != null) { |
| try { |
| getContainer().run(true, true, new IRunnableWithProgress() { |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { |
| new SelectionModelStateSerializer(getCatalog(), getSelectionModel()).deserialize( |
| getConfiguration().getInitialState(), getConfiguration().getInitialOperations(), |
| monitor); |
| } |
| }); |
| } catch (InvocationTargetException e) { |
| throw new CoreException(MarketplaceClientCore.computeStatus(e, Messages.MarketplaceViewer_unexpectedException)); |
| } catch (InterruptedException e) { |
| // user canceled |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| List<Entry<CatalogItem, Operation>> itemToSelectedOperation = new ArrayList<Entry<CatalogItem, Operation>>( |
| getSelectionModel().getItemToSelectedOperation().entrySet()); |
| final List<CatalogItem> noninstallableItems = new ArrayList<CatalogItem>(); |
| for (Entry<CatalogItem, Operation> entry : itemToSelectedOperation) { |
| if (entry.getValue() != Operation.NONE) { |
| boolean unavailableInstall = (Boolean.FALSE.equals(entry.getKey().getAvailable()) || entry.getKey() |
| .getSiteUrl() == null) |
| && (entry.getValue() == Operation.INSTALL || entry.getValue() == Operation.UPDATE); |
| if (unavailableInstall) { |
| getSelectionModel().select(entry.getKey(), Operation.NONE); |
| noninstallableItems.add(entry.getKey()); |
| } else { |
| entry.getKey().setSelected(true); |
| } |
| } |
| } |
| if (!noninstallableItems.isEmpty()) { |
| notifyNonInstallableItems(noninstallableItems); |
| } |
| } |
| } |
| |
| protected void notifyNonInstallableItems(final List<CatalogItem> noninstallableItems) { |
| MessageDialog dialog = new MessageDialog(getShell(), Messages.MarketplaceWizard_UnableToInstallSolutions, null, Messages.MarketplaceWizard_IncompatibleSolutionsMessage, MessageDialog.ERROR, |
| new String[] { IDialogConstants.OK_LABEL }, 0) { |
| @Override |
| protected Control createCustomArea(Composite parent) { |
| parent.setLayout(new GridLayout()); |
| TableViewer tableViewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL |
| | SWT.BORDER); |
| tableViewer.setLabelProvider(createLabelProvider()); |
| tableViewer.setContentProvider(createContentProvider()); |
| tableViewer.setInput(noninstallableItems); |
| Control tableControl = tableViewer.getControl(); |
| GridDataFactory.fillDefaults() |
| .grab(true, true) |
| .align(SWT.FILL, SWT.FILL) |
| .hint(SWT.DEFAULT, |
| Math.max(40, |
| Math.min(120, tableControl.computeSize(SWT.DEFAULT, SWT.DEFAULT).y))) |
| .applyTo(tableControl); |
| return tableControl; |
| } |
| |
| private LabelProvider createLabelProvider() { |
| return new LabelProvider() { |
| @Override |
| public String getText(Object element) { |
| //TODO it would be great to know the compatible version range |
| //we could show that with an IStyledLabelProvider behind the name |
| return ((CatalogItem) element).getName(); |
| } |
| |
| @Override |
| public Image getImage(Object element) { |
| return MarketplaceClientUiPlugin.getInstance() |
| .getImageRegistry() |
| .get(MarketplaceClientUiPlugin.IU_ICON_ERROR); |
| } |
| }; |
| } |
| |
| private IStructuredContentProvider createContentProvider() { |
| return new IStructuredContentProvider() { |
| |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| } |
| |
| public void dispose() { |
| } |
| |
| public Object[] getElements(Object inputElement) { |
| return ((List<?>) inputElement).toArray(); |
| } |
| }; |
| } |
| }; |
| dialog.open(); |
| } |
| |
| boolean wantInitializeInitialSelection() { |
| if (initialSelectionInitialized) { |
| return false; |
| } else if (getConfiguration().getInitialState() != null || getConfiguration().getInitialOperations() != null) { |
| return true; |
| } else if (initialState != null) { |
| WizardState initialState = getInitialState(); |
| if ((initialState.getContent() != null && !initialState.getContent().isEmpty()) |
| || (initialState.getContentType() != null && initialState.getContentType() != ContentType.SEARCH) |
| || (initialState.getFilterCategory() != null || initialState.getFilterMarket() != null || initialState.getFilterQuery() != null)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean canFinish() { |
| if (computeMustCheckLicenseAcceptance()) { |
| if (acceptLicensesPage == null && getContainer().getCurrentPage() == getFeatureSelectionWizardPage()) { |
| getNextPage(getFeatureSelectionWizardPage(), false); |
| } |
| if (acceptLicensesPage == null || !acceptLicensesPage.isPageComplete()) { |
| return false; |
| } |
| } |
| if (profileChangeOperation != null) { |
| IStatus resolutionResult = profileChangeOperation.getResolutionResult(); |
| switch (resolutionResult.getSeverity()) { |
| case IStatus.OK: |
| case IStatus.WARNING: |
| case IStatus.INFO: |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public IWizardPage getNextPage(IWizardPage page) { |
| return getNextPage(page, true); |
| } |
| |
| IWizardPage getNextPage(IWizardPage page, boolean nextpressed) { |
| boolean skipFeatureSelection = false; |
| MarketplacePage catalogPage = getCatalogPage(); |
| if (page == catalogPage && nextpressed) { |
| profileChangeOperation = null; |
| featureSelectionWizardPage.updateMessage(); |
| if (catalogPage.canSkipFeatureSelection()) { |
| skipFeatureSelection = true; |
| } |
| } |
| if (page == featureSelectionWizardPage || (page == catalogPage && skipFeatureSelection)) { |
| if (nextpressed && profileChangeOperation != null |
| && profileChangeOperation instanceof RemediationOperation) { |
| |
| try { |
| getContainer().run(true, false, new IRunnableWithProgress() { |
| public void run(IProgressMonitor monitor) { |
| ((RemediationOperation) profileChangeOperation).setCurrentRemedy(featureSelectionWizardPage.getRemediationGroup() |
| .getCurrentRemedy()); |
| profileChangeOperation.resolveModal(monitor); |
| } |
| }); |
| } catch (InterruptedException e) { |
| // Nothing to report if thread was interrupted |
| } catch (InvocationTargetException e) { |
| ProvUI.handleException(e.getCause(), null, StatusManager.SHOW | StatusManager.LOG); |
| } |
| } |
| IWizardPage nextPage = null; |
| boolean operationUpdated = false; |
| if (profileChangeOperation == null) { |
| if (nextpressed) { |
| updateProfileChangeOperation(); |
| operationUpdated = true; |
| } |
| if (profileChangeOperation == null || !profileChangeOperation.getResolutionResult().isOK()) { |
| // can't compute a change operation, so there must be some kind of error |
| // we show these on the the feature selection wizard page |
| nextPage = featureSelectionWizardPage; |
| } else if (profileChangeOperation instanceof UninstallOperation) { |
| // next button was used to resolve errors on an uninstall. |
| // by returning the same page the finish button will be enabled, allowing the user to finish. |
| nextPage = featureSelectionWizardPage; |
| } else if (profileChangeOperation instanceof RemediationOperation) { |
| nextPage = featureSelectionWizardPage; |
| } |
| } |
| if (nextPage == null && nextpressed && profileChangeOperation instanceof RemediationOperation |
| && !featureSelectionWizardPage.isInRemediationMode()) { |
| featureSelectionWizardPage.flipToRemediationComposite(); |
| nextPage = featureSelectionWizardPage; |
| } |
| if (nextPage == null && computeMustCheckLicenseAcceptance()) { |
| if (acceptLicensesPage == null) { |
| acceptLicensesPage = new AcceptLicensesWizardPage( |
| ProvisioningUI.getDefaultUI().getLicenseManager(), operationIUs, profileChangeOperation); |
| addPage(acceptLicensesPage); |
| } else { |
| acceptLicensesPage.update(operationIUs, profileChangeOperation); |
| } |
| if (nextpressed || acceptLicensesPage.hasLicensesToAccept() |
| || profileChangeOperation instanceof RemediationOperation) { |
| nextPage = acceptLicensesPage; |
| } |
| } |
| if (nextPage == null && page == catalogPage) { |
| nextPage = featureSelectionWizardPage; |
| } |
| if (operationUpdated && nextPage == getContainer().getCurrentPage()) { |
| getContainer().updateButtons(); |
| } |
| return nextPage; |
| } |
| if (page instanceof ImportFavoritesPage) { |
| return catalogPage; |
| } |
| return getNextPageInList(page); |
| } |
| |
| protected IWizardPage getNextPageInList(IWizardPage page) { |
| return super.getNextPage(page); |
| } |
| |
| public boolean computeMustCheckLicenseAcceptance() { |
| return profileChangeOperation != null && !(profileChangeOperation instanceof UninstallOperation); |
| } |
| |
| @Override |
| public void addPages() { |
| doDefaultCatalogSelection(); |
| super.addPages(); |
| featureSelectionWizardPage = new FeatureSelectionWizardPage(); |
| addPage(featureSelectionWizardPage); |
| } |
| |
| FeatureSelectionWizardPage getFeatureSelectionWizardPage() { |
| return featureSelectionWizardPage; |
| } |
| |
| |
| @Override |
| public IWizardPage getStartingPage() { |
| if (getConfiguration().getCatalogDescriptor() != null) { |
| if (wantInitializeInitialSelection()) { |
| WizardState initialState = getInitialState(); |
| if (initialState == null || !Boolean.FALSE.equals(initialState.getProceedWithInstallation())) { |
| return getFeatureSelectionWizardPage(); |
| } |
| } |
| return getCatalogPage(); |
| } |
| return super.getStartingPage(); |
| } |
| |
| private void doDefaultCatalogSelection() { |
| if (getConfiguration().getCatalogDescriptor() == null) { |
| String defaultCatalogUrl = MarketplaceClientUiPlugin.getInstance() |
| .getPreferenceStore() |
| .getString(PREF_DEFAULT_CATALOG); |
| // if a preferences was set, we default to that catalog descriptor |
| if (defaultCatalogUrl != null && defaultCatalogUrl.length() > 0) { |
| for (CatalogDescriptor descriptor : getConfiguration().getCatalogDescriptors()) { |
| URL url = descriptor.getUrl(); |
| try { |
| if (url.toURI().toString().equals(defaultCatalogUrl)) { |
| getConfiguration().setCatalogDescriptor(descriptor); |
| break; |
| } |
| } catch (URISyntaxException e) { |
| // ignore |
| } |
| } |
| } |
| // if no catalog is selected, pick one |
| if (getConfiguration().getCatalogDescriptor() == null |
| && getConfiguration().getCatalogDescriptors().size() > 0) { |
| // fall back to first catalog |
| getConfiguration().setCatalogDescriptor(getConfiguration().getCatalogDescriptors().get(0)); |
| } |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| removeAddedRepositoryLocations(); |
| if (getConfiguration().getCatalogDescriptor() != null) { |
| // remember the catalog for next time. |
| try { |
| MarketplaceClientUiPlugin.getInstance() |
| .getPreferenceStore() |
| .setValue(PREF_DEFAULT_CATALOG, |
| getConfiguration().getCatalogDescriptor().getUrl().toURI().toString()); |
| } catch (URISyntaxException e) { |
| // ignore |
| } |
| } |
| if (getCatalog() != null) { |
| getCatalog().dispose(); |
| } |
| super.dispose(); |
| } |
| |
| @Override |
| public boolean performFinish() { |
| if (profileChangeOperation != null |
| && profileChangeOperation.getResolutionResult().getSeverity() != IStatus.ERROR) { |
| if (computeMustCheckLicenseAcceptance()) { |
| if (acceptLicensesPage != null && acceptLicensesPage.isPageComplete()) { |
| acceptLicensesPage.performFinish(); |
| } |
| } |
| ProvisioningJob provisioningJob = profileChangeOperation.getProvisioningJob(null); |
| if (provisioningJob != null) { |
| if (!operationNewInstallItems.isEmpty()) { |
| provisioningJob.addJobChangeListener(new ProvisioningJobListener(operationNewInstallItems)); |
| } |
| ProvisioningUI.getDefaultUI().schedule(provisioningJob, StatusManager.SHOW | StatusManager.LOG); |
| addedRepositoryLocations = null; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public MarketplacePage getCatalogPage() { |
| return (MarketplacePage) super.getCatalogPage(); |
| } |
| |
| public synchronized Set<String> getInstalledFeatures() { |
| if (installedFeatures == null) { |
| try { |
| if (Display.getCurrent() != null) { |
| getContainer().run(true, false, new IRunnableWithProgress() { |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, |
| InterruptedException { |
| installedFeatures = MarketplaceClientUi.computeInstalledFeatures(monitor); |
| } |
| }); |
| } else { |
| installedFeatures = MarketplaceClientUi.computeInstalledFeatures(new NullProgressMonitor()); |
| } |
| } catch (InvocationTargetException e) { |
| MarketplaceClientUi.error(e.getCause()); |
| installedFeatures = Collections.emptySet(); |
| } catch (InterruptedException e) { |
| // should never happen (not cancelable) |
| throw new IllegalStateException(e); |
| } |
| } |
| return installedFeatures; |
| } |
| |
| public SelectionModel getSelectionModel() { |
| return selectionModel; |
| } |
| |
| public void openUrl(String url) { |
| String catalogUrl = getCatalogUrl(); |
| if (WorkbenchBrowserSupport.getInstance().isInternalWebBrowserAvailable() |
| && url.toLowerCase().startsWith(catalogUrl.toLowerCase())) { |
| int style = IWorkbenchBrowserSupport.AS_EDITOR | IWorkbenchBrowserSupport.LOCATION_BAR |
| | IWorkbenchBrowserSupport.NAVIGATION_BAR; |
| String browserId = "MPC-" + catalogUrl.replaceAll("[^a-zA-Z0-9_-]", "_"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| try { |
| CatalogDescriptor catalogDescriptor = getConfiguration().getCatalogDescriptor(); |
| IWebBrowser browser = WorkbenchBrowserSupport.getInstance().createBrowser(style, browserId, |
| catalogDescriptor.getLabel(), catalogDescriptor.getDescription()); |
| final String originalUrl = url; |
| url = appendWizardState(url); |
| browser.openURL(new URL(url)); // ORDER DEPENDENCY //don't encode/validate URL - browser can be quite lenient |
| PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().setActive(); |
| if (!hookLocationListener(browser)) { // ORDER DEPENDENCY |
| browser.openURL(new URL(originalUrl)); |
| } |
| } catch (PartInitException e) { |
| MarketplaceClientUi.handle(e.getStatus(), |
| StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG); |
| } catch (MalformedURLException e) { |
| IStatus status = new Status(IStatus.ERROR, MarketplaceClientUi.BUNDLE_ID, NLS.bind( |
| Messages.MarketplaceWizard_cannotOpenUrl, new Object[] { url, e.getMessage() }), e); |
| MarketplaceClientUi.handle(status, StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG); |
| } |
| } else { |
| WorkbenchUtil.openUrl(url, IWorkbenchBrowserSupport.AS_EXTERNAL); |
| } |
| } |
| |
| private String getCatalogUrl() { |
| CatalogDescriptor catalogDescriptor = getConfiguration().getCatalogDescriptor(); |
| URL catalogUrl = catalogDescriptor.getUrl(); |
| URI catalogUri; |
| try { |
| catalogUri = catalogUrl.toURI(); |
| } catch (URISyntaxException e) { |
| // should never happen |
| throw new IllegalStateException(e); |
| } |
| return catalogUri.toString(); |
| } |
| |
| private String appendWizardState(String url) { |
| try { |
| if (url.indexOf('?') == -1) { |
| url += '?'; |
| } else { |
| url += '&'; |
| } |
| String state = new SelectionModelStateSerializer(getCatalog(), getSelectionModel()).serialize(); |
| url += "mpc=true&mpc_state=" + URLEncoder.encode(state, "UTF-8"); //$NON-NLS-1$//$NON-NLS-2$ |
| return url; |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); // should never happen |
| } |
| } |
| |
| private boolean hookLocationListener(IWebBrowser webBrowser) { |
| try { |
| Field partField = findField(webBrowser.getClass(), "part", IWorkbenchPart.class); //$NON-NLS-1$ |
| if (partField != null) { |
| partField.setAccessible(true); |
| IWorkbenchPart part = (IWorkbenchPart) partField.get(webBrowser); |
| if (part != null) { |
| Field browserViewerField = findField(part.getClass(), "webBrowser", null); //$NON-NLS-1$ |
| if (browserViewerField != null) { |
| browserViewerField.setAccessible(true); |
| Object browserViewer = browserViewerField.get(part); |
| if (browserViewer != null) { |
| Field browserField = findField(browserViewer.getClass(), "browser", Browser.class); //$NON-NLS-1$ |
| if (browserField != null) { |
| browserField.setAccessible(true); |
| Browser browser = (Browser) browserField.get(browserViewer); |
| if (browser != null) { |
| // only hook the listener once |
| if (browser.getData(MarketplaceBrowserIntegration.class.getName()) == null) { |
| if (browserListener == null) { |
| browserListener = new MarketplaceBrowserIntegration(); |
| } |
| browser.setData(MarketplaceBrowserIntegration.class.getName(), browserListener); |
| // hook in listeners |
| browser.addLocationListener(browserListener); |
| browser.addOpenWindowListener(browserListener); |
| } |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } |
| } catch (Throwable t) { |
| // ignore |
| } |
| return false; |
| } |
| |
| private Field findField(Class<?> clazz, String fieldName, Class<?> fieldClass) { |
| while (clazz != Object.class) { |
| for (Field field : clazz.getDeclaredFields()) { |
| if (field.getName().equals(fieldName) |
| && (fieldClass == null || fieldClass.isAssignableFrom(field.getType()))) { |
| return field; |
| } |
| } |
| clazz = clazz.getSuperclass(); |
| } |
| return null; |
| } |
| |
| public void updateProfileChangeOperation() { |
| removeAddedRepositoryLocations(); |
| addedRepositoryLocations = null; |
| profileChangeOperation = null; |
| operationIUs = null; |
| IWizardContainer wizardContainer = getContainer(); |
| if (getSelectionModel().computeProvisioningOperationViable()) { |
| ProfileChangeOperationComputer provisioningOperation = null; |
| try { |
| final Map<CatalogItem, Operation> itemToOperation = getSelectionModel().getItemToSelectedOperation(); |
| final Set<CatalogItem> selectedItems = getSelectionModel().getSelectedCatalogItems(); |
| OperationType operationType = null; |
| for (Map.Entry<CatalogItem, Operation> entry : itemToOperation.entrySet()) { |
| if (!selectedItems.contains(entry.getKey())) { |
| continue; |
| } |
| OperationType entryOperationType = OperationType.map(entry.getValue()); |
| if (entryOperationType != null) { |
| if (operationType == null || operationType == OperationType.UPDATE || entryOperationType == OperationType.CHANGE) { |
| operationType = entryOperationType; |
| } |
| } |
| } |
| Map<FeatureEntry, Operation> featureEntries = getSelectionModel().getFeatureEntryToOperation(false, |
| false); |
| if (operationType == OperationType.CHANGE || operationType == OperationType.UPDATE) { |
| Set<OperationType> featureOperations = EnumSet.noneOf(OperationType.class); |
| for (Entry<FeatureEntry, Operation> entry : featureEntries.entrySet()) { |
| OperationType operation = OperationType.map(entry.getValue()); |
| if (operation != null) { |
| featureOperations.add(operation); |
| } |
| } |
| if (featureOperations.contains(OperationType.INSTALL) |
| && featureOperations.contains(OperationType.UPDATE)) { |
| //just perform install instead, which covers update |
| featureOperations.remove(OperationType.UPDATE); |
| } |
| if (featureOperations.size() == 1) { |
| operationType = featureOperations.iterator().next(); |
| } |
| } |
| URI dependenciesRepository = null; |
| if (getConfiguration().getCatalogDescriptor().getDependenciesRepository() != null) { |
| try { |
| dependenciesRepository = getConfiguration().getCatalogDescriptor() |
| .getDependenciesRepository() |
| .toURI(); |
| } catch (URISyntaxException e) { |
| throw new InvocationTargetException(e); |
| } |
| } |
| provisioningOperation = new ProfileChangeOperationComputer( |
| operationType, |
| selectedItems, |
| featureEntries.keySet(), |
| dependenciesRepository, |
| getConfiguration().getCatalogDescriptor().isInstallFromAllRepositories() ? ProfileChangeOperationComputer.ResolutionStrategy.FALLBACK_STRATEGY |
| : ProfileChangeOperationComputer.ResolutionStrategy.SELECTED_REPOSITORIES, |
| withRemediation); |
| wizardContainer.run(true, true, provisioningOperation); |
| |
| profileChangeOperation = provisioningOperation.getOperation(); |
| operationIUs = provisioningOperation.getIus(); |
| addedRepositoryLocations = provisioningOperation.getAddedRepositoryLocations(); |
| operationNewInstallItems = computeNewInstallCatalogItems(); |
| errorMessage = provisioningOperation.getErrorMessage(); |
| |
| final IStatus result = profileChangeOperation.getResolutionResult(); |
| if (result != null && operationIUs != null && operationIUs.length > 0 |
| && operationType == OperationType.INSTALL) { |
| switch (result.getSeverity()) { |
| case IStatus.ERROR: |
| Job job = new Job(Messages.MarketplaceWizard_errorNotificationJob) { |
| IStatus r = result; |
| |
| Set<CatalogItem> items = new HashSet<CatalogItem>(itemToOperation.keySet()); |
| |
| IInstallableUnit[] ius = operationIUs; |
| |
| String details = profileChangeOperation.getResolutionDetails(); |
| { |
| setSystem(false); |
| setUser(false); |
| setPriority(LONG); |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| getCatalog().installErrorReport(monitor, r, items, ius, details); |
| return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS; |
| } |
| }; |
| job.schedule(); |
| } |
| } |
| |
| } catch (InvocationTargetException e) { |
| Throwable cause = e.getCause(); |
| IStatus status; |
| if (cause instanceof CoreException) { |
| status = ((CoreException) cause).getStatus(); |
| } else { |
| status = new Status(IStatus.ERROR, MarketplaceClientUi.BUNDLE_ID, NLS.bind( |
| Messages.MarketplaceWizard_problemsPerformingProvisioningOperation, |
| new Object[] { cause.getMessage() }), cause); |
| } |
| MarketplaceClientUi.handle(status, StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG); |
| } catch (InterruptedException e) { |
| // canceled |
| MarketplaceClientUi.log(IStatus.CANCEL, MarketplaceClientUi.BUNDLE_ID, |
| Messages.MarketplaceWizard_ProvisioningOperationCancelled, e); |
| } finally { |
| if (provisioningOperation != null) { |
| addedRepositoryLocations = provisioningOperation.getAddedRepositoryLocations(); |
| } |
| } |
| } |
| //re-get the container - in case the wizard was closed in the meantime, this will be null... |
| wizardContainer = getContainer(); |
| if (wizardContainer != null && wizardContainer.getCurrentPage() == featureSelectionWizardPage) { |
| featureSelectionWizardPage.updateMessage(); |
| } |
| } |
| |
| private void removeAddedRepositoryLocations() { |
| AbstractProvisioningOperation.removeRepositoryLocations(addedRepositoryLocations); |
| addedRepositoryLocations = null; |
| } |
| |
| private Set<CatalogItem> computeNewInstallCatalogItems() { |
| Set<CatalogItem> items = new HashSet<CatalogItem>(); |
| Map<CatalogItem, Collection<String>> iusByCatalogItem = new HashMap<CatalogItem, Collection<String>>(); |
| for (CatalogItemEntry entry : getSelectionModel().getCatalogItemEntries()) { |
| List<FeatureEntry> features = entry.getChildren(); |
| Collection<String> featureIds = new ArrayList<String>(features.size()); |
| for (FeatureEntry feature : features) { |
| if (feature.computeChangeOperation() == Operation.INSTALL) { |
| featureIds.add(feature.getFeatureDescriptor().getId()); |
| } |
| } |
| if (!featureIds.isEmpty()) { |
| iusByCatalogItem.put(entry.getItem(), featureIds); |
| } |
| } |
| for (IInstallableUnit unit : operationIUs) { |
| for (Entry<CatalogItem, Collection<String>> entry : iusByCatalogItem.entrySet()) { |
| if (entry.getValue().contains(unit.getId())) { |
| items.add(entry.getKey()); |
| } |
| } |
| } |
| return items; |
| } |
| |
| void initializeCatalog() { |
| final MarketplaceCatalog catalog = getCatalog(); |
| synchronized (catalog) { |
| List<AbstractDiscoveryStrategy> discoveryStrategies = catalog.getDiscoveryStrategies(); |
| for (AbstractDiscoveryStrategy strategy : discoveryStrategies) { |
| strategy.dispose(); |
| } |
| discoveryStrategies.clear(); |
| if (getConfiguration().getCatalogDescriptor() != null) { |
| MarketplaceDiscoveryStrategy discoveryStrategy = new MarketplaceDiscoveryStrategy( |
| getConfiguration().getCatalogDescriptor()); |
| discoveryStrategy.setShellProvider(this); |
| discoveryStrategies.add(discoveryStrategy); |
| |
| } |
| } |
| } |
| |
| protected void updateNews() { |
| CatalogDescriptor catalogDescriptor = getConfiguration().getCatalogDescriptor(); |
| INews news = null; |
| if (Boolean.parseBoolean(Platform.getDebugOption(DEBUG_NEWS_FLAG))) { |
| // use debug override values |
| String debugNewsUrl = Platform.getDebugOption(DEBUG_NEWS_URL); |
| if (debugNewsUrl != null && debugNewsUrl.length() > 0) { |
| News debugNews = new News(); |
| news = debugNews; |
| debugNews.setUrl(debugNewsUrl); |
| String debugNewsTitle = Platform.getDebugOption(DEBUG_NEWS_TITLE); |
| if (debugNewsTitle == null || debugNewsTitle.length() == 0) { |
| debugNews.setShortTitle("Debug News"); //$NON-NLS-1$ |
| } else { |
| debugNews.setShortTitle(debugNewsTitle); |
| } |
| debugNews.setTimestamp(System.currentTimeMillis()); |
| } |
| } |
| if (news == null) { |
| // try requesting news from marketplace |
| try { |
| final INews[] result = new INews[1]; |
| getContainer().run(true, true, new IRunnableWithProgress() { |
| |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { |
| IStatus status = getCatalog().performNewsDiscovery(monitor); |
| if (!status.isOK() && status.getSeverity() != IStatus.CANCEL) { |
| // don't bother user with missing news |
| MarketplaceClientUi.handle(status, StatusManager.LOG); |
| } |
| result[0] = getCatalog().getNews(); |
| } |
| }); |
| if (result[0] != null) { |
| news = result[0]; |
| } |
| } catch (InvocationTargetException e) { |
| final IStatus status = MarketplaceClientCore.computeStatus(e, Messages.MarketplaceViewer_unexpectedException); |
| MarketplaceClientUi.handle(status, StatusManager.LOG); |
| } catch (InterruptedException e) { |
| // cancelled by user |
| } |
| } |
| if (news == null) { |
| // use news from catalog |
| news = CatalogRegistry.getInstance().getCatalogNews(catalogDescriptor); |
| } |
| CatalogRegistry.getInstance().addCatalogNews(catalogDescriptor, news); |
| } |
| |
| public Object suspendWizard() { |
| String catalogUrl = getCatalogUrl(); |
| String key = appendWizardState(catalogUrl); |
| getContainer().getShell().close(); |
| return key; |
| } |
| |
| public void setInitialState(WizardState initialState) { |
| this.initialState = initialState; |
| } |
| |
| public WizardState getInitialState() { |
| return initialState; |
| } |
| |
| public static void resumeWizard(Display display, Object state, boolean proceedWithInstall) { |
| String catalogUrl = (String) state; |
| if (proceedWithInstall) { |
| org.eclipse.epp.mpc.ui.MarketplaceUrlHandler.SolutionInstallationInfo installInfo = MarketplaceUrlHandler.createSolutionInstallInfo(catalogUrl); |
| if (installInfo != null) { |
| MarketplaceUrlHandler.triggerInstall(installInfo); |
| return; |
| } |
| } |
| CatalogDescriptor descriptor = catalogUrl == null ? null : CatalogRegistry.getInstance().findCatalogDescriptor( |
| catalogUrl); |
| final MarketplaceWizardCommand command = new MarketplaceWizardCommand(); |
| if (descriptor != null) { |
| descriptor = new CatalogDescriptor(descriptor); |
| descriptor.setLabel(MarketplaceUrlHandler.DESCRIPTOR_HINT); |
| command.setSelectedCatalogDescriptor(descriptor); |
| } |
| String mpcState = MarketplaceUrlHandler.getMPCState(catalogUrl); |
| if (mpcState != null && mpcState.length() > 0) { |
| try { |
| command.setWizardState(URLDecoder.decode(mpcState, "UTF-8")); //$NON-NLS-1$ |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); // should never happen |
| } |
| if (!proceedWithInstall) { |
| WizardState wizardState = new WizardState(); |
| wizardState.setProceedWithInstallation(false); |
| command.setWizardDialogState(wizardState); |
| } |
| } |
| display.asyncExec(new Runnable() { |
| |
| public void run() { |
| try { |
| command.execute(new ExecutionEvent()); |
| } catch (ExecutionException e) { |
| IStatus status = MarketplaceClientCore.computeStatus(e, Messages.MarketplaceBrowserIntegration_cannotOpenMarketplaceWizard); |
| MarketplaceClientUi.handle(status, |
| StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG); |
| } |
| } |
| }); |
| } |
| } |