/******************************************************************************* * Copyright (c) 2014, 2015 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.server.git.servlets; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION; import static org.eclipse.jgit.lib.Constants.DOT_GIT_MODULES; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.RmCommand; import org.eclipse.jgit.api.SubmoduleAddCommand; import org.eclipse.jgit.api.SubmoduleInitCommand; import org.eclipse.jgit.api.SubmoduleStatusCommand; import org.eclipse.jgit.api.SubmoduleSyncCommand; import org.eclipse.jgit.api.SubmoduleUpdateCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.submodule.SubmoduleStatus; import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; import org.eclipse.orion.internal.server.servlets.ServletResourceHandler; import org.eclipse.orion.server.core.OrionConfiguration; import org.eclipse.orion.server.core.ProtocolConstants; import org.eclipse.orion.server.core.ServerStatus; import org.eclipse.orion.server.core.metastore.IMetaStore; import org.eclipse.orion.server.core.metastore.ProjectInfo; import org.eclipse.orion.server.core.metastore.WorkspaceInfo; import org.eclipse.orion.server.git.GitConstants; import org.eclipse.orion.server.git.servlets.GitUtils.Traverse; import org.eclipse.osgi.util.NLS; import org.json.JSONObject; public class GitSubmoduleHandlerV1 extends AbstractGitHandler { GitSubmoduleHandlerV1(ServletResourceHandler<IStatus> statusHandler) { super(statusHandler); } @Override protected boolean handlePost(RequestInfo requestInfo) throws ServletException { JSONObject toAdd = requestInfo.getJSONRequest(); HttpServletRequest request = requestInfo.request; HttpServletResponse response = requestInfo.response; Repository db = requestInfo.db; try { String targetPath = null; String gitUrl = toAdd.optString(GitConstants.KEY_URL,null); String name = toAdd.optString(ProtocolConstants.KEY_NAME,null); if(gitUrl!=null){ String workspacePath = ServletResourceHandler.toOrionLocation(request, toAdd.optString(ProtocolConstants.KEY_LOCATION, null)); // expected path /file/{workspaceId}/{projectName}[/{path}] String filePathString = ServletResourceHandler.toOrionLocation(request, toAdd.optString(ProtocolConstants.KEY_PATH, null)); IPath filePath = filePathString == null ? null : new Path(filePathString); if (filePath != null && filePath.segmentCount() < 3) filePath = null; if (filePath == null && workspacePath == null) { String msg = NLS.bind("Either {0} or {1} should be provided: {2}", new Object[] { ProtocolConstants.KEY_PATH, ProtocolConstants.KEY_LOCATION, toAdd }); return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null)); } ProjectInfo project = null; if (filePath != null) { // path format is /file/{workspaceId}/{projectName}/[filePath] project = GitUtils.projectFromPath(filePath); // workspace path format needs to be used if project does not exist if (project == null) { String msg = NLS.bind("Specified project does not exist: {0}", filePath.segment(2)); return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null)); } if (name == null) name = project.getContentLocation().relativize(project.getProjectStore().getFileStore(filePath.removeFirstSegments(3)).toURI()).toString(); } else if (workspacePath != null) { // TODO: move this to CloneJob // if so, modify init part to create a new project if necessary if (name == null){ IPath path = new Path(workspacePath); final IMetaStore metaStore = OrionConfiguration.getMetaStore(); WorkspaceInfo workspace = metaStore.readWorkspace(path.segment(1)); name = new URIish(gitUrl).getHumanishName(); name = GitUtils.getUniqueProjectName(workspace, name); } }else{ return false; } targetPath = targetPath == null? name:targetPath; addSubmodules(db, gitUrl,targetPath); }else{ return false; } return true; } catch (Exception ex) { String msg = "An error occured for add submodule command."; return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, ex)); } } @Override protected boolean handlePut(RequestInfo requestInfo) throws ServletException { JSONObject requestPayload = requestInfo.getJSONRequest(); HttpServletRequest request = requestInfo.request; HttpServletResponse response = requestInfo.response; Repository db = requestInfo.db; try { String operation = requestPayload.optString("Operation",null); if(operation==null){ return false; } else if(operation.equals("update")){ return updateSubmodules(db); }else if(operation.equals("sync")){ return syncSubmodules(db); } return false; } catch (Exception ex) { String msg = "An error occured for update submodule command."; return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, ex)); } } @Override protected boolean handleDelete(RequestInfo requestInfo) throws ServletException { HttpServletRequest request = requestInfo.request; HttpServletResponse response = requestInfo.response; Repository db = requestInfo.db; Repository parentRepo = null; try { Map<IPath, File> parents = GitUtils.getGitDirs(requestInfo.filePath.removeLastSegments(1), Traverse.GO_UP); if (parents.size() < 1) return false; parentRepo = FileRepositoryBuilder.create(parents.entrySet().iterator().next().getValue()); String pathToSubmodule = db.getWorkTree().toString().substring(parentRepo.getWorkTree().toString().length() + 1); removeSubmodule(db, parentRepo, pathToSubmodule); return true; } catch (Exception ex) { String msg = "An error occured for delete submodule command."; return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, ex)); }finally{ if (parentRepo != null) { parentRepo.close(); } } } public static boolean updateSubmodules(Repository repo) throws GitAPIException { SubmoduleInitCommand init = new SubmoduleInitCommand(repo); init.call(); SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(repo); Collection<String> updated = update.call(); SubmoduleStatusCommand status = new SubmoduleStatusCommand(repo); Map<String, SubmoduleStatus> statusResult = status.call(); return updated.size() == statusResult.size(); } public static boolean syncSubmodules(Repository repo) throws GitAPIException { SubmoduleSyncCommand sync = new SubmoduleSyncCommand(repo); Map<String, String> synced = sync.call(); SubmoduleStatusCommand status = new SubmoduleStatusCommand(repo); Map<String, SubmoduleStatus> statusResult = status.call(); return synced.size() == statusResult.size(); } public static void addSubmodules(Repository repo, String targetUrl, String targetPath) throws GitAPIException { SubmoduleAddCommand addCommand = new SubmoduleAddCommand(repo); addCommand.setURI(targetUrl); addCommand.setPath(targetPath); Repository repository = addCommand.call(); repository.close(); } public static void removeSubmodule(Repository db, Repository parentRepo, String pathToSubmodule) throws Exception { pathToSubmodule = pathToSubmodule.replace("\\", "/"); StoredConfig gitSubmodulesConfig = getGitSubmodulesConfig(parentRepo); gitSubmodulesConfig.unsetSection(CONFIG_SUBMODULE_SECTION, pathToSubmodule); gitSubmodulesConfig.save(); StoredConfig repositoryConfig = parentRepo.getConfig(); repositoryConfig.unsetSection(CONFIG_SUBMODULE_SECTION, pathToSubmodule); repositoryConfig.save(); Git git = Git.wrap(parentRepo); git.add().addFilepattern(DOT_GIT_MODULES).call(); RmCommand rm = git.rm().addFilepattern(pathToSubmodule); if (gitSubmodulesConfig.getSections().size() == 0) { rm.addFilepattern(DOT_GIT_MODULES); } rm.call(); FileUtils.delete(db.getWorkTree(), FileUtils.RECURSIVE); FileUtils.delete(db.getDirectory(), FileUtils.RECURSIVE); } private static StoredConfig getGitSubmodulesConfig( Repository repository ) throws IOException, ConfigInvalidException { File gitSubmodulesFile = new File( repository.getWorkTree(), DOT_GIT_MODULES ); FileBasedConfig gitSubmodulesConfig = new FileBasedConfig( null, gitSubmodulesFile, FS.DETECTED ); gitSubmodulesConfig.load(); return gitSubmodulesConfig; } }