/******************************************************************************* * Copyright (c) 2015 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.openshift.internal.ui.wizard.importapp; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jgit.api.CheckoutResult; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.osgi.util.NLS; import org.jboss.ide.eclipse.as.core.util.RegExUtils; import org.jboss.tools.foundation.core.plugin.log.StatusFactory; import org.jboss.tools.openshift.egit.core.EGitUtils; import org.jboss.tools.openshift.egit.ui.util.EGitUIUtils; import org.jboss.tools.openshift.internal.common.ui.application.importoperation.GeneralProjectImportOperation; import org.jboss.tools.openshift.internal.common.ui.application.importoperation.MavenProjectImportOperation; import org.jboss.tools.openshift.internal.common.ui.application.importoperation.WontOverwriteException; import org.jboss.tools.openshift.internal.core.OpenShiftCoreActivator; import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator; import com.openshift.restclient.OpenShiftException; /** * @author André Dietisheim <adietish@redhat.com> */ public class ImportProjectOperation { private static final String PLATFORM_SEPARATOR = Matcher.quoteReplacement(File.separator); private File cloneDestination; private String gitUrl; private String gitRef; private Collection<String> filters; private boolean reuseGitRepo; private boolean checkoutBranch; /** * Constructor to skip the clone and simply import the filtered projects from the destination * @param gitRef the git ref (branch) to import * @param cloneDestination the destination to clone to * @param filters the project to import */ public ImportProjectOperation(String gitUrl, String gitRef, File cloneDestination, Collection<String> filters, boolean checkoutBranch) { this(gitUrl, gitRef, cloneDestination, filters, checkoutBranch, true); } /** * Constructor to both clone the repository and import the filtered projects * @param gitUrl the git repo to clone from * @param gitRef the git ref (branch) to import * @param cloneDestination the destination to clone to * @param filters the project to import */ public ImportProjectOperation(String gitUrl, String gitRef, File cloneDestination, Collection<String> filters) { this(gitUrl, gitRef, cloneDestination, filters, true, false); } protected ImportProjectOperation(String gitUrl, String gitRef, File cloneDestination, Collection<String> filters, boolean checkoutBranch, boolean reuseGitRepo) { this.gitUrl = gitUrl; this.gitRef = gitRef; this.cloneDestination = cloneDestination; this.filters = sanitize(filters); this.reuseGitRepo = reuseGitRepo; this.checkoutBranch = checkoutBranch; } /** * Imports the (new) project that the user has chosen into the workspace. * * @param monitor * the monitor to report progress to * @throws OpenShiftException * @throws CoreException * @throws InterruptedException * @throws URISyntaxException * @throws InvocationTargetException * @throws IOException * @throws GitAPIException * @throws NoWorkTreeException */ public void execute(IProgressMonitor monitor) throws OpenShiftException, CoreException, InterruptedException, URISyntaxException, InvocationTargetException, IOException, NoWorkTreeException, GitAPIException { if (!reuseGitRepo) { importCloning(gitUrl, gitRef, cloneDestination, filters, monitor); } else { importReusingExistingRepo(gitUrl, gitRef, cloneDestination, filters, checkoutBranch, monitor); } } private void importCloning(String gitUrl, String gitRef, File cloneDestination, Collection<String> filters, IProgressMonitor monitor) throws CoreException, OpenShiftException, InvocationTargetException, InterruptedException, URISyntaxException { if (cloneDestinationExists()) { throw new WontOverwriteException(NLS.bind( "There's already a folder at {0}. The new OpenShift project would overwrite it. " + "Please choose another destination to clone to.", getCloneDestination().getAbsolutePath())); } File repositoryFolder = cloneRepository(gitUrl, cloneDestination, gitRef, monitor); List<IProject> importedProjects = importProjectsFrom(repositoryFolder, filters, monitor); connectToGitRepo(importedProjects, repositoryFolder, monitor); } private void importReusingExistingRepo(String gitUrl, String gitRef, File repositoryFolder, Collection<String> filters, boolean checkoutBranch, IProgressMonitor monitor) throws CoreException, InterruptedException { List<IProject> importedProjects = importProjectsFrom(repositoryFolder, filters, monitor); connectToGitRepo(importedProjects, repositoryFolder, monitor); if (checkoutBranch) { checkoutBranch(gitRef, gitUrl, importedProjects, repositoryFolder, monitor); } } protected void checkoutBranch(String gitRef, String gitUrl, List<IProject> importedProjects, File repositoryFolder, IProgressMonitor monitor) throws CoreException { if (!StringUtils.isEmpty(gitRef) && !CollectionUtils.isEmpty(importedProjects)) { // all projects are in the same repo IProject project = importedProjects.get(0); Repository repository = EGitUtils.getRepository(project); try { if (!EGitUtils.hasBranch(gitRef, repository)) { Pattern gitURIPattern = Pattern.compile(RegExUtils.escapeRegex(gitUrl)); RemoteConfig remote = EGitUtils.getRemoteByUrl(gitURIPattern, repository); fetchBranch(gitRef, remote, repository, monitor); } CheckoutResult result = EGitUtils.checkoutBranch(gitRef, repository, monitor); switch(result.getStatus()) { case CONFLICTS: case ERROR: throw new CoreException(StatusFactory.errorStatus(OpenShiftCoreActivator.PLUGIN_ID, NLS.bind("Could not check out the branch {0} of the (reused) local repository at {1} because of {2}." + " Please resolve the problem and check out the branch manually so that it matches the code that's used in your OpenShift application.", new Object[] { gitRef, repositoryFolder, result.getStatus().toString().toLowerCase() }))); default: } } catch (IOException e) { throw new CoreException(StatusFactory.errorStatus(OpenShiftCoreActivator.PLUGIN_ID, NLS.bind("Could check that branch {0} exists within the (reused) local repository at {1}.", gitRef, repositoryFolder), e)); } catch (InvocationTargetException e) { throw new CoreException(StatusFactory.errorStatus(OpenShiftCoreActivator.PLUGIN_ID, NLS.bind("Could not fetch branch {0} from check that branch {0} exists within the (reused) local repository at {1}.", gitRef, repositoryFolder), e)); } } } /** * Fetches and creates the branch with the given name from the given remote. * @param gitRef * @param project * @param repository * @param monitor * @throws CoreException * @throws InvocationTargetException * @throws IOException */ private void fetchBranch(String gitRef, RemoteConfig remote, Repository repository, IProgressMonitor monitor) throws CoreException, InvocationTargetException, IOException { if (remote == null) { throw new CoreException(StatusFactory.errorStatus(OpenShiftUIActivator.PLUGIN_ID, NLS.bind("Could not fetch determine the remote for the repo at {0} that we should fetch branch {1} from.", repository.getDirectory(), gitRef))); } EGitUtils.fetch(remote, Arrays.asList(new RefSpec(Constants.R_HEADS + gitRef + ":" + Constants.R_REMOTES + remote.getName() + "/" + gitRef)), repository, monitor); RevCommit commit = EGitUtils.getLatestCommit(gitRef, remote.getName(), repository); EGitUtils.createBranch(gitRef, commit, repository, monitor); } /** * Imports the projects that are within the given folder. Supports maven and * general projects * * @param folder * the folder the projects are located in * @param filters * @param monitor * the monitor to report progress to * @return * @throws CoreException * @throws InterruptedException */ private List<IProject> importProjectsFrom(final File folder, Collection<String> filters, IProgressMonitor monitor) throws CoreException, InterruptedException { MavenProjectImportOperation mavenImport = new MavenProjectImportOperation(folder); mavenImport.setFilters(filters); List<IProject> importedProjects; if (mavenImport.isMavenProject()) { importedProjects = mavenImport.importToWorkspace(monitor); } else { importedProjects = new GeneralProjectImportOperation(folder).importToWorkspace(monitor); } return importedProjects; } protected void connectToGitRepo(List<IProject> projects, File projectFolder, IProgressMonitor monitor) throws CoreException { for (IProject project : projects) { if (project != null) { EGitUtils.connect(project, monitor); } } } /** * Clones the repository of the selected OpenShift application to the user * provided path. * * @param application * the application to clone * @param remoteName * the name of the remote repo to clone * @param destination * the destination to clone to * @param gitRef * the git reference to check-out * @param addToRepoView * if true, the clone repo will get added to the (egit) * repositories view * @param monitor * the monitor to report progress to * * @return the location of the cloned repository * @throws OpenShiftException * @throws InvocationTargetException * @throws InterruptedException * @throws URISyntaxException * * @see AbstractImportApplicationOperation#getApplication() * @see #getCloneDestination() */ protected File cloneRepository(String gitUrl, File destination, String gitRef, IProgressMonitor monitor) throws OpenShiftException, InvocationTargetException, InterruptedException, URISyntaxException { monitor.subTask(NLS.bind("Cloning {0}...", gitUrl)); EGitUIUtils.ensureEgitUIIsStarted(); EGitUtils.cloneRepository( gitUrl, Constants.DEFAULT_REMOTE_NAME, gitRef, destination, EGitUIUtils.ADD_TO_REPOVIEW_TASK, monitor); return destination; } protected File getCloneDestination() { return cloneDestination; } protected boolean cloneDestinationExists() { return cloneDestination != null && cloneDestination.exists(); } private static Collection<String> sanitize(Collection<String> filters) { Collection<String> sanitized = null; if (filters != null) { sanitized = new LinkedHashSet<>(filters.size()); for (String path : filters) { if (StringUtils.isNotBlank(path)) { sanitized.add(makePlatformDependent(path)); } } } return sanitized; } private static String makePlatformDependent(String path) { return path.replaceAll("/", PLATFORM_SEPARATOR) .replaceAll("\\\\", PLATFORM_SEPARATOR); } }