/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, inc..
* 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:
* Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.ui.actions;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import com.salesforce.ide.api.metadata.types.Package;
import com.salesforce.ide.core.factories.FactoryException;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.MessageDialogRunnable;
import com.salesforce.ide.core.internal.utils.Messages;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.model.Component;
import com.salesforce.ide.core.model.ProjectPackageList;
import com.salesforce.ide.core.project.ForceProjectException;
import com.salesforce.ide.core.remote.Connection;
import com.salesforce.ide.core.remote.ForceConnectionException;
import com.salesforce.ide.core.remote.ForceRemoteException;
import com.salesforce.ide.core.remote.metadata.RetrieveResultExt;
import com.salesforce.ide.core.services.ServiceException;
import com.salesforce.ide.core.services.ServiceTimeoutException;
public class RefreshResourceActionController extends ActionController {
private static final Logger logger = Logger.getLogger(RefreshResourceActionController.class);
private static final String OPERATION = "refresh resource(s)";
private boolean refreshResult = true;
// C O N S T R U C T O R S
public RefreshResourceActionController() throws ForceProjectException {
super();
}
// M E T H O D S
@Override
public WizardDialog getWizardDialog() {
return null;
}
@Override
public boolean preRun(IAction action) {
if (Utils.isEmpty(selectedResources)) {
logger.info("Operation cancelled. Resources not provided.");
return false;
}
// skip if not a force managed resource
if (!getProjectService().isManagedResources(selectedResources)) {
Utils.openError("Not Managed Resource", "Unable to refresh resource '" + getSelectedResource().getName()
+ "'. Resource is not a " + Constants.PRODUCT_NAME + " resource.");
return false;
}
for (IResource selectedResource : selectedResources) {
// if dirty, ask user what he/she wants to do
if (checkForDirtyResource(selectedResource)) {
StringBuffer strBuffer = new StringBuffer("One or more of the resources or their child resources ");
strBuffer.append("you are trying to refresh is open, unsaved, and/or dirty.\n\n").append(
"Continue to refresh this resource replacing it with the latest from Salesforce?");
boolean result = Utils.openQuestion("Confirm Refresh Open File", strBuffer.toString());
if (!result) {
if (logger.isInfoEnabled()) {
logger.info("User cancelled. File is open for edit or dirty.");
}
return false;
}
}
}
return true;
}
/**
* Refresh given resources fetching latest instance(s) from org and saving to project
*
* @param monitor
* @return
* @throws InterruptedException
* @throws ForceConnectionException
* @throws FactoryException
* @throws CoreException
* @throws IOException
* @throws ForceRemoteException
* @throws InvocationTargetException
* @throws ServiceException
* @throws MetadataServiceException
*/
public boolean refreshResources(IProgressMonitor monitor) throws InterruptedException, ForceConnectionException,
FactoryException, CoreException, IOException, ForceRemoteException, InvocationTargetException,
ServiceException, Exception {
if (Utils.isEmpty(selectedResources)) {
logger.warn("Unable to refresh resources - resources is null or empty");
return false;
}
if (logger.isDebugEnabled()) {
StringBuffer strBuffer =
new StringBuffer("Refreshing the following resource roots [" + selectedResources.size() + "]:");
int resourceCnt = 0;
for (IResource resource : selectedResources) {
strBuffer.append("\n (").append(++resourceCnt).append(") ").append(
resource.getProjectRelativePath().toPortableString());
}
logger.debug(strBuffer.toString());
}
monitorCheck(monitor);
monitorSubTask(monitor, "Retrieving remote components...");
monitorCheck(monitor);
// refresh at project root - fetch and save everything and be done w/ it
if (Utils.isNotEmpty(getProjectService().getResourcesByType(selectedResources, IResource.PROJECT))) {
IFolder folder = getProjectService().getSourceFolder(project);
if (!refreshSourceFolder(folder, monitor)) {
failRefreshResult();
}
// must fetch separately due to returned package structure - referenced packages
// maintains the package-prefixed hierarchy
if (!retrieveInstalledPackages(project, monitor)) {
failRefreshResult();
}
// project level refresh takes care of everything
return refreshResult;
}
// handle if source root was selected
handleSourceRefresh(monitor);
// if only source root was selected was selected, let's end here
if (selectedResources.size() == 1
&& Utils.isNotEmpty(getProjectService().getFolder(selectedResources, Constants.SOURCE_FOLDER_NAME))) {
return refreshResult;
}
// handle source component folders
handleSourceComponentFolderRefresh(monitor);
// handle source component files
handleSourceComponentFileRefresh(monitor);
// handle if referenced packages dir was selected
handleReferencePackagesRefresh(monitor);
// handle if individual/multiple reference package folder was selected
handleReferencePackageFolderRefresh(monitor);
if (logger.isDebugEnabled()) {
logger.debug("Overall refresh process was" + (refreshResult ? " " : " NOT") + " successful");
}
return refreshResult;
}
protected void handleSourceRefresh(IProgressMonitor monitor) throws InterruptedException, ForceConnectionException,
ForceRemoteException, FactoryException, CoreException, IOException, InvocationTargetException,
ServiceException, Exception {
monitorCheck(monitor);
if (Utils.isNotEmpty(getProjectService().getFolder(selectedResources, Constants.SOURCE_FOLDER_NAME))) {
IFolder sourceFolder = getProjectService().getSourceFolder(project);
if (!refreshSourceFolder(sourceFolder, monitor)) {
logger.warn("Unable to refresh source root");
failRefreshResult();
}
}
}
// refresh folder
private boolean refreshSourceFolder(IFolder folder, IProgressMonitor monitor) throws FactoryException,
ForceConnectionException, CoreException, InterruptedException, IOException, ForceRemoteException,
InvocationTargetException, ServiceException, Exception {
if (folder == null || !folder.exists()) {
return false;
}
if (logger.isDebugEnabled()) {
logger.debug("Refreshing folder '" + folder.getProjectRelativePath().toPortableString() + "' resource");
}
String packageName = getProjectService().getPackageName(project);
// perform retrieve
RetrieveResultExt retrieveResultHandler = null;
try {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().retrievePackage(project, packageName, monitor);
} catch (ServiceTimeoutException ex) {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().handleRetrieveServiceTimeoutException(ex, OPERATION,
monitor);
}
if (retrieveResultHandler == null) {
logger.warn("Unable to refresh folder '" + folder.getName() + "' - retrieve result is null");
return false;
}
monitorWork(monitor);
ProjectPackageList projectPackageList =
getProjectPackageFactory().getProjectPackageListInstance(new String[] { packageName });
projectPackageList.setProject(project);
// handle result including messages and saving resources
return getProjectService().handleRetrieveResult(projectPackageList, retrieveResultHandler, true, null, monitor);
}
protected void handleReferencePackagesRefresh(IProgressMonitor monitor) throws InterruptedException,
ForceConnectionException, ForceRemoteException, FactoryException, CoreException, IOException,
InvocationTargetException, ServiceException, Exception {
monitorCheck(monitor);
IFolder referencePkgFolder =
getProjectService().getFolder(selectedResources, Constants.REFERENCED_PACKAGE_FOLDER_NAME);
if (referencePkgFolder != null) {
if (!retrieveInstalledPackages(project, monitor)) {
logger.warn("Unable to refresh installed packages");
failRefreshResult();
}
}
}
protected void handleReferencePackageFolderRefresh(IProgressMonitor monitor) throws InterruptedException,
ForceConnectionException, ForceRemoteException, FactoryException, CoreException, ServiceException,
IOException, InvocationTargetException, Exception {
monitorCheck(monitor);
List<IResource> folders = getProjectService().getResourcesByType(selectedResources, IResource.FOLDER);
List<String> pkgNames = new ArrayList<String>();
if (Utils.isNotEmpty(folders)) {
for (IResource folder : folders) {
if (getProjectService().isReferencedPackageFolder(folder)) {
// set reference pkg folder contents to readonly=false so save of retrieve content
// doesn't prompt with 'unable to write'
Utils.adjustResourceReadOnly(folder, false, true);
pkgNames.add(folder.getName());
}
}
if (Utils.isNotEmpty(pkgNames)) {
RetrieveResultExt retrieveResultHandler = null;
try {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().retrieveInstalledPackages(project,
pkgNames.toArray(new String[pkgNames.size()]), monitor);
retrieveResultHandler.getProjectPackageList().setProject(project);
} catch (ServiceTimeoutException ex) {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().handleRetrieveServiceTimeoutException(ex,
OPERATION, monitor);
if (retrieveResultHandler != null) {
ProjectPackageList projectPackageList =
getProjectPackageFactory().getProjectPackageListInstance(
pkgNames.toArray(new String[pkgNames.size()]));
if (projectPackageList != null) {
projectPackageList.setProject(project);
}
retrieveResultHandler.setProjectPackageList(projectPackageList);
}
}
if (retrieveResultHandler == null
|| !getProjectService().handleRetrieveResult(retrieveResultHandler, true, monitor)) {
logger.warn("Unable to refresh component folders");
failRefreshResult();
}
}
}
}
protected boolean retrieveInstalledPackages(IProject project, IProgressMonitor monitor)
throws InterruptedException, ForceConnectionException, ForceRemoteException, FactoryException,
CoreException, InvocationTargetException, IOException, ServiceException, Exception {
if (project == null) {
throw new IllegalArgumentException("Project cannot be null");
}
if (logger.isInfoEnabled()) {
logger.info("Fetching and saving all installed, managed components for '" + project.getName() + "'");
}
// set reference pkg folder contents to readonly=false so save of retrieve content
// doesn't prompt with 'unable to write'
IFolder referencePkgFolder = getProjectService().getReferencedPackagesFolder(project);
Utils.adjustResourceReadOnly(referencePkgFolder, false, true);
monitorCheck(monitor);
RetrieveResultExt retrieveResultHandler = null;
try {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().retrieveManagedInstalledPackages(project, monitor);
} catch (ServiceTimeoutException ex) {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().handleRetrieveServiceTimeoutException(ex, OPERATION,
monitor);
if (retrieveResultHandler != null) {
Connection connection = getConnectionFactory().getConnection(project);
ProjectPackageList projectPackageList =
getProjectPackageFactory().getManagedInstalledProjectPackages(connection);
if (projectPackageList != null) {
projectPackageList.setProject(project);
}
retrieveResultHandler.setProjectPackageList(projectPackageList);
}
}
if (retrieveResultHandler == null) {
logger.warn("Unable to refresh installed packages - retrieve result is null");
return false;
}
if (retrieveResultHandler.getZipFileCount() == 0) {
return true;
}
if (Utils.isNotEmpty(retrieveResultHandler.getProjectPackageList())) {
retrieveResultHandler.getProjectPackageList().setProject(project);
}
monitorCheck(monitor);
return getProjectService().handleRetrieveResult(retrieveResultHandler.getProjectPackageList(),
retrieveResultHandler, true, null, monitor);
}
protected void handleSourceComponentFolderRefresh(IProgressMonitor monitor) throws InterruptedException,
ForceConnectionException, ForceRemoteException, FactoryException, CoreException, IOException,
InvocationTargetException, ServiceException, Exception {
monitorCheck(monitor);
List<IResource> folders = getProjectService().getResourcesByType(selectedResources, IResource.FOLDER);
List<String> componentTypes = new ArrayList<String>();
if (Utils.isNotEmpty(folders)) {
for (IResource folder : folders) {
if (getProjectService().isComponentFolder(folder)) {
Component component = getComponentFactory().getComponentByFolderName(folder.getName());
componentTypes.add(component.getComponentType());
} else if (getProjectService().isSubComponentFolder(folder)) {
Component component = getComponentFactory().getComponentFromSubFolder((IFolder) folder, false);
componentTypes.add(component.getSecondaryComponentType());
}
// reference package folders is handled in methods specific for reference packages.
}
if (Utils.isNotEmpty(componentTypes)) {
// Bug #206315: In package project, prompt user that refresh on component folder is not support, and
// either cancel or refresh from src.
Package packageManifest = getPackageManifestFactory().getPackageManifest(getProject());
if (Utils.isEmpty(packageManifest)) {
throw new FactoryException(Messages.getString(
"Retrieve.PackageProject.RefreshComponentFolder.MissPackageManifest.Exception.message",
new String[] { getProject().getName() }));
}
if (Utils.isNotEmpty(packageManifest.getFullName())) {
String dialogMsg =
Messages.getString("Retrieve.PackageProject.RefreshComponentFolder.Dialog.message");
MessageDialogRunnable messageDialogRunnable =
new MessageDialogRunnable("Refresh from server", null, dialogMsg,
MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL,
IDialogConstants.CANCEL_LABEL }, 0);
Display.getDefault().syncExec(messageDialogRunnable);
if (messageDialogRunnable.getAction() == Window.CANCEL) {
String cancelMsg =
Messages.getString("Retrieve.PackageProject.RefreshComponentFolder.Cancel.message");
logger.info(cancelMsg);
throw new InterruptedException(cancelMsg);
}
}
// refresh component dirs
ProjectPackageList projectPackageList =
getProjectPackageFactory().getProjectPackageListInstance(project);
// only save these types
String[] saveComponentTypes = componentTypes.toArray(new String[componentTypes.size()]);
// perform retrieve
RetrieveResultExt retrieveResultHandler = null;
try {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().retrieveSelective(projectPackageList,
saveComponentTypes, monitor);
} catch (ServiceTimeoutException ex) {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().handleRetrieveServiceTimeoutException(ex,
OPERATION, monitor);
}
if (retrieveResultHandler == null
|| !getProjectService().handleRetrieveResult(projectPackageList, retrieveResultHandler, true,
saveComponentTypes, monitor)) {
logger.warn("Unable to refresh component folders");
failRefreshResult();
}
}
}
}
protected void handleSourceComponentFileRefresh(IProgressMonitor monitor) throws InterruptedException,
ForceConnectionException, ForceRemoteException, FactoryException, CoreException, IOException,
InvocationTargetException, ServiceException, Exception {
monitorCheck(monitor);
List<IResource> files = getProjectService().getResourcesByType(selectedResources, IResource.FILE);
if (Utils.isNotEmpty(files)) {
List<IResource> sourceResources = new ArrayList<IResource>(files.size());
for (IResource file : files) {
if (getProjectService().isSourceResource(file)) {
sourceResources.add(file);
}
}
ProjectPackageList projectPackageList = getProjectService().getProjectContents(sourceResources, monitor);
projectPackageList.setProject(project);
monitorCheck(monitor);
RetrieveResultExt retrieveResultHandler = null;
try {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().retrieveSelective(projectPackageList, true, monitor);
} catch (ServiceTimeoutException ex) {
retrieveResultHandler =
getServiceLocator().getPackageRetrieveService().handleRetrieveServiceTimeoutException(ex, OPERATION,
monitor);
}
if (retrieveResultHandler == null
|| !getProjectService().handleRetrieveResult(retrieveResultHandler, true, monitor)) {
logger.warn("Unable to refresh component files");
failRefreshResult();
}
}
}
@Override
public void postRun(IAction action) {}
private boolean checkForDirtyResource(IResource resource) {
try {
if (checkForMarkers(resource)) {
return true;
}
if (resource.getType() == IResource.FILE && checkForOpenFileWork((IFile) resource)) {
return true;
} else if (resource.getType() == IResource.FOLDER) {
return checkForOpenFile((IFolder) resource);
} else if (resource.getType() == IResource.PROJECT) {
IFolder srcFolder = getProjectService().getSourceFolder(project);
return checkForOpenFile(srcFolder);
}
} catch (CoreException e) {
String logMessage = Utils.generateCoreExceptionLog(e);
logger.warn("Unable to test for markers on " + resource.getProjectRelativePath().toPortableString() + ": "
+ logMessage, e);
}
return false;
}
private boolean checkForMarkers(IResource resource) throws CoreException {
if (resource == null || !resource.exists()) {
return false;
}
IMarker[] markers = resource.findMarkers("com.salesforce.ide.core.problem", true, IResource.DEPTH_INFINITE);
// no markers, let's go
if (Utils.isEmpty(markers)) {
return false;
}
// ignore package.xml markers - typically general warnings are applied to package.xml and the probably
// will not overwrite it anyway
if (markers.length == 1 && getProjectService().isPackageManifestFile(markers[0].getResource())) {
return false;
}
// markers were found, let's warn
return true;
}
private boolean checkForOpenFile(IFolder folder) throws CoreException {
if (folder == null || Utils.isEmpty(folder.members())) {
return false;
}
IResource[] members = folder.members();
for (IResource resource : members) {
if (resource.getType() == IResource.FILE && checkForOpenFileWork((IFile) resource)) {
return true;
} else if (resource.getType() == IResource.FOLDER) {
return checkForOpenFile((IFolder) resource);
}
}
return false;
}
private boolean checkForOpenFileWork(IFile file) {
IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
if (Utils.isNotEmpty(windows)) {
// loop thru open windows to check if file is open if open, check w/ user
for (IWorkbenchWindow window : windows) {
IWorkbenchPage page = window.getActivePage();
IEditorPart editor = page.getActiveEditor();
if (editor != null && editor instanceof IFileEditorInput) {
IFileEditorInput fei = (IFileEditorInput) editor.getEditorInput();
IFile editFile = fei.getFile();
if (file.equals(editFile)) {
return true;
}
}
}
}
return false;
}
private void failRefreshResult() {
if (refreshResult) {
refreshResult = false;
}
}
}