org.eclipse.orion.server.git.servlets.GitCloneHandlerV1.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.orion.server.git.servlets.GitCloneHandlerV1.java

Source

/*******************************************************************************
 * Copyright (c) 2011 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 java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.core.runtime.*;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.CheckoutResult.Status;
import org.eclipse.jgit.api.CheckoutResult.Status;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.orion.internal.server.servlets.*;
import org.eclipse.orion.internal.server.servlets.workspace.*;
import org.eclipse.orion.internal.server.servlets.workspace.authorization.AuthorizationService;
import org.eclipse.orion.server.core.ServerStatus;
import org.eclipse.orion.server.core.tasks.TaskInfo;
import org.eclipse.orion.server.git.GitConstants;
import org.eclipse.orion.server.git.GitCredentialsProvider;
import org.eclipse.orion.server.git.servlets.GitUtils.Traverse;
import org.eclipse.orion.server.servlets.OrionServlet;
import org.eclipse.orion.server.user.profile.IOrionUserProfileConstants;
import org.eclipse.orion.server.user.profile.IOrionUserProfileNode;
import org.eclipse.orion.server.useradmin.UserServiceHelper;
import org.eclipse.osgi.util.NLS;
import org.json.*;

/**
 * A handler for Git Clone operation.
 */
public class GitCloneHandlerV1 extends ServletResourceHandler<String> {

    private ServletResourceHandler<IStatus> statusHandler;

    GitCloneHandlerV1(ServletResourceHandler<IStatus> statusHandler) {
        this.statusHandler = statusHandler;
    }

    @Override
    public boolean handleRequest(HttpServletRequest request, HttpServletResponse response, String path)
            throws ServletException {
        try {
            switch (getMethod(request)) {
            case GET:
                return handleGet(request, response, path);
            case PUT:
                return handlePut(request, response, path);
            case POST:
                return handlePost(request, response);
            case DELETE:
                return handleDelete(request, response, path);
            }

        } catch (Exception e) {
            String msg = NLS.bind("Failed to handle /git/clone request for {0}", path);
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, e));
        }
        return false;
    }

    private boolean handlePost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, JSONException, ServletException, URISyntaxException, CoreException, NoHeadException,
            NoMessageException, ConcurrentRefUpdateException, JGitInternalException, WrongRepositoryStateException {
        // make sure required fields are set
        JSONObject toAdd = OrionServlet.readJSONRequest(request);
        WebClone clone = new WebClone();
        String url = toAdd.optString(GitConstants.KEY_URL, null);
        // method handles repository clone or just repository init
        // decision is based on existence of GitUrl argument 
        boolean initOnly;
        if (url == null || url.isEmpty())
            initOnly = true;
        else {
            initOnly = false;
            if (!validateCloneUrl(url, request, response))
                return true;
            clone.setUrl(new URIish(url));
        }
        String cloneName = toAdd.optString(ProtocolConstants.KEY_NAME, null);
        if (cloneName == null)
            cloneName = request.getHeader(ProtocolConstants.HEADER_SLUG);
        // expected path /workspace/{workspaceId}
        String workspacePath = toAdd.optString(ProtocolConstants.KEY_LOCATION, null);
        // expected path /file/{projectId}[/{path}]
        String filePath = toAdd.optString(ProtocolConstants.KEY_PATH, 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));
        }
        // only during init operation filePath or cloneName must be provided
        // during clone operation, name can be obtained from URL
        if (initOnly && filePath == null && cloneName == null) {
            String msg = NLS.bind("Either {0} or {1} should be provided: {2}",
                    new Object[] { ProtocolConstants.KEY_PATH, GitConstants.KEY_NAME, toAdd });
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
        }
        if (!validateCloneName(cloneName, request, response))
            return true;

        // prepare the WebClone object, create a new project if necessary
        WebProject webProject = null;
        boolean webProjectExists = false;
        if (filePath != null) {
            IPath path = new Path(filePath);
            clone.setId(path.removeFirstSegments(1).toString());
            webProject = WebProject.fromId(path.segment(1));
            webProjectExists = WebProject.exists(path.segment(1));
            clone.setContentLocation(
                    webProject.getProjectStore().getFileStore(path.removeFirstSegments(2)).toURI());
            if (cloneName == null)
                cloneName = path.segmentCount() > 2 ? path.lastSegment() : webProject.getName();
        } else if (workspacePath != null) {
            IPath path = new Path(workspacePath);
            // TODO: move this to CloneJob
            // if so, modify init part to create a new project if necessary
            String id = WebProject.nextProjectId();
            webProjectExists = false;
            webProject = WebProject.fromId(id);
            if (cloneName == null)
                cloneName = new URIish(url).getHumanishName();
            webProject.setName(cloneName);

            try {
                WorkspaceResourceHandler.computeProjectLocation(webProject, null, request.getRemoteUser(), false);
            } catch (CoreException e) {
                //we are unable to write in the platform location!
                String msg = NLS.bind("Server content location could not be written: {0}",
                        Activator.getDefault().getRootLocationURI());
                return statusHandler.handleRequest(request, response,
                        new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, e));
            } catch (URISyntaxException e) {
                // should not happen, we do not allow linking at this point
            }
            WebWorkspace workspace = WebWorkspace.fromId(path.segment(1));
            //If all went well, add project to workspace
            workspace.addProject(webProject);

            //save the workspace and project metadata
            try {
                webProject.save();
                workspace.save();
            } catch (CoreException e) {
                return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error persisting project state", e));
            }

            URI baseLocation = getURI(request);
            baseLocation = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), workspacePath, baseLocation.getQuery(), baseLocation.getFragment());
            JSONObject jsonProject = WebProjectResourceHandler.toJSON(webProject, baseLocation);
            // give the clone creator access rights to the new project
            addProjectRights(request, response, jsonProject.optString(ProtocolConstants.KEY_LOCATION));
            addProjectRights(request, response, jsonProject.optString(ProtocolConstants.KEY_CONTENT_LOCATION));

            clone.setId(webProject.getId());
            clone.setContentLocation(webProject.getProjectStore().toURI());
        }
        clone.setName(cloneName);
        JSONObject cloneObject = WebClone.toJSON(clone, getURI(request));
        String cloneLocation = cloneObject.getString(ProtocolConstants.KEY_LOCATION);

        if (initOnly) {
            // git init
            InitJob job = new InitJob(clone, request.getRemoteUser(), cloneLocation);
            job.schedule();
            TaskInfo task = job.getTask();
            JSONObject result = task.toJSON();
            // Not nice that the git service knows the location of the task servlet, but task service doesn't know this either
            String taskLocation = getURI(request).resolve("../../task/id/" + task.getTaskId()).toString(); //$NON-NLS-1$
            result.put(ProtocolConstants.KEY_LOCATION, taskLocation);
            response.setHeader(ProtocolConstants.HEADER_LOCATION, taskLocation);
            OrionServlet.writeJSONResponse(request, response, result);
            response.setStatus(HttpServletResponse.SC_ACCEPTED);
            return true;
        } else {
            // git clone
            // prepare creds
            String username = toAdd.optString(GitConstants.KEY_USERNAME, null);
            char[] password = toAdd.optString(GitConstants.KEY_PASSWORD, "").toCharArray(); //$NON-NLS-1$
            String knownHosts = toAdd.optString(GitConstants.KEY_KNOWN_HOSTS, null);
            byte[] privateKey = toAdd.optString(GitConstants.KEY_PRIVATE_KEY, "").getBytes(); //$NON-NLS-1$
            byte[] publicKey = toAdd.optString(GitConstants.KEY_PUBLIC_KEY, "").getBytes(); //$NON-NLS-1$
            byte[] passphrase = toAdd.optString(GitConstants.KEY_PASSPHRASE, "").getBytes(); //$NON-NLS-1$

            GitCredentialsProvider cp = new GitCredentialsProvider(new URIish(clone.getUrl()), username, password,
                    knownHosts);
            cp.setPrivateKey(privateKey);
            cp.setPublicKey(publicKey);
            cp.setPassphrase(passphrase);

            // if all went well, clone
            CloneJob job = new CloneJob(clone, cp, request.getRemoteUser(), cloneLocation,
                    webProjectExists ? null : webProject /* used for cleaning up, so null when not needed */);
            job.schedule();
            TaskInfo task = job.getTask();
            JSONObject result = task.toJSON();
            // Not nice that the git service knows the location of the task servlet, but task service doesn't know this either
            String taskLocation = getURI(request).resolve("../../task/id/" + task.getTaskId()).toString(); //$NON-NLS-1$
            result.put(ProtocolConstants.KEY_LOCATION, taskLocation);
            response.setHeader(ProtocolConstants.HEADER_LOCATION, taskLocation);
            OrionServlet.writeJSONResponse(request, response, result);
            response.setStatus(HttpServletResponse.SC_ACCEPTED);
            return true;
        }
    }

    static void doConfigureClone(Git git, String user) throws IOException, CoreException {
        StoredConfig config = git.getRepository().getConfig();
        IOrionUserProfileNode userNode = UserServiceHelper.getDefault().getUserProfileService()
                .getUserProfileNode(user, true).getUserProfileNode(IOrionUserProfileConstants.GENERAL_PROFILE_PART);
        if (userNode.get(GitConstants.KEY_NAME, null) != null)
            config.setString(ConfigConstants.CONFIG_USER_SECTION, null, ConfigConstants.CONFIG_KEY_NAME,
                    userNode.get(GitConstants.KEY_NAME, null));
        if (userNode.get(GitConstants.KEY_MAIL, null) != null)
            config.setString(ConfigConstants.CONFIG_USER_SECTION, null, ConfigConstants.CONFIG_KEY_EMAIL,
                    userNode.get(GitConstants.KEY_MAIL, null));
        config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_FILEMODE, false);
        config.save();
    }

    private boolean handleGet(HttpServletRequest request, HttpServletResponse response, String pathString)
            throws IOException, JSONException, ServletException, URISyntaxException, CoreException {
        IPath path = pathString == null ? Path.EMPTY : new Path(pathString);
        URI baseLocation = getURI(request);
        String user = request.getRemoteUser();
        // expected path format is 'workspace/{workspaceId}' or 'file/{projectId}[/{path}]'
        if (path.segment(0).equals("workspace") && path.segmentCount() == 2) { //$NON-NLS-1$
            // all clones in the workspace
            if (WebWorkspace.exists(path.segment(1))) {
                WebWorkspace workspace = WebWorkspace.fromId(path.segment(1));
                JSONArray projects = workspace.getProjectsJSON();
                JSONObject result = new JSONObject();
                JSONArray children = new JSONArray();
                for (int i = 0; i < projects.length(); i++) {
                    try {
                        JSONObject project = (JSONObject) projects.get(i);
                        //this is the location of the project metadata
                        WebProject webProject = WebProject.fromId(project.getString(ProtocolConstants.KEY_ID));
                        if (isAccessAllowed(user, webProject)) {
                            URI contentLocation = URI.create(webProject.getId());
                            IPath projectPath = new Path(contentLocation.getPath());
                            Map<IPath, File> gitDirs = GitUtils.getGitDirs(projectPath, Traverse.GO_DOWN);
                            for (Map.Entry<IPath, File> entry : gitDirs.entrySet()) {
                                JSONObject child = toJSON(entry, baseLocation);
                                children.put(child);
                            }
                        }
                    } catch (JSONException e) {
                        //ignore malformed children
                    }
                }
                result.put(ProtocolConstants.KEY_CHILDREN, children);
                OrionServlet.writeJSONResponse(request, response, result);
                return true;
            } else {
                String msg = NLS.bind("Nothing found for the given ID: {0}", path);
                return statusHandler.handleRequest(request, response,
                        new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, null));
            }
        } else if (path.segment(0).equals("file") && path.segmentCount() > 1) { //$NON-NLS-1$
            // clones under given path
            WebProject webProject = WebProject.fromId(path.segment(1));
            if (isAccessAllowed(user, webProject) && webProject.getProjectStore()
                    .getFileStore(path.removeFirstSegments(2)).fetchInfo().exists()) {
                URI contentLocation = URI.create(webProject.getId());
                IPath projectPath = new Path(contentLocation.getPath()).append(path.removeFirstSegments(2));
                Map<IPath, File> gitDirs = GitUtils.getGitDirs(projectPath, Traverse.GO_DOWN);
                JSONObject result = new JSONObject();
                JSONArray children = new JSONArray();
                for (Map.Entry<IPath, File> entry : gitDirs.entrySet()) {
                    JSONObject child = toJSON(entry, baseLocation);
                    children.put(child);
                }
                result.put(ProtocolConstants.KEY_CHILDREN, children);
                OrionServlet.writeJSONResponse(request, response, result);
                return true;
            } else {
                String msg = NLS.bind("Nothing found for the given ID: {0}", path);
                return statusHandler.handleRequest(request, response,
                        new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, null));
            }
        }
        //else the request is malformed
        String msg = NLS.bind("Invalid clone request: {0}", path);
        return statusHandler.handleRequest(request, response,
                new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
    }

    private boolean handlePut(HttpServletRequest request, HttpServletResponse response, String pathString)
            throws IOException, JSONException, ServletException, URISyntaxException, CoreException,
            JGitInternalException, GitAPIException {
        IPath path = pathString == null ? Path.EMPTY : new Path(pathString);
        if (path.segment(0).equals("file") && path.segmentCount() > 1) { //$NON-NLS-1$

            // make sure a clone is addressed
            WebProject webProject = WebProject.fromId(path.segment(1));
            if (isAccessAllowed(request.getRemoteUser(), webProject)) {
                URI contentLocation = URI.create(webProject.getId());
                IPath projectPath = new Path(contentLocation.getPath()).append(path.removeFirstSegments(2));
                projectPath = path.hasTrailingSeparator() ? projectPath : projectPath.removeLastSegments(1);
                File gitDir = GitUtils.getGitDirs(new Path("file").append(projectPath), Traverse.CURRENT).values() //$NON-NLS-1$
                        .iterator().next();

                // make sure required fields are set
                JSONObject toCheckout = OrionServlet.readJSONRequest(request);
                JSONArray paths = toCheckout.optJSONArray(ProtocolConstants.KEY_PATH);
                String branch = toCheckout.optString(GitConstants.KEY_BRANCH_NAME);
                if ((paths == null || paths.length() == 0) && (branch == null || branch.isEmpty())) {
                    String msg = NLS.bind("Either '{0}' or '{1}' should be provided: {2}",
                            new Object[] { ProtocolConstants.KEY_PATH, GitConstants.KEY_BRANCH_NAME, toCheckout });
                    return statusHandler.handleRequest(request, response,
                            new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
                }

                Git git = new Git(new FileRepository(gitDir));
                if (paths != null) {
                    CheckoutCommand checkout = git.checkout();
                    for (int i = 0; i < paths.length(); i++) {
                        checkout.addPath(paths.getString(i));
                    }
                    checkout.call();
                    return true;
                } else if (branch != null) {
                    CheckoutCommand co = git.checkout();
                    try {
                        co.setName(branch).call();
                        return true;
                    } catch (JGitInternalException e) {
                        if (Status.CONFLICTS.equals(co.getResult().getStatus())) {
                            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                                    HttpServletResponse.SC_CONFLICT, "Checkout aborted.", e));
                        }
                        // TODO: handle other exceptions
                    } catch (RefNotFoundException e) {
                        String msg = NLS.bind("Branch name not found: {0}", branch);
                        return statusHandler.handleRequest(request, response,
                                new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, e));
                    }

                }
            } else {
                String msg = NLS.bind("Nothing found for the given ID: {0}", path);
                return statusHandler.handleRequest(request, response,
                        new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, null));
            }
        }
        String msg = NLS.bind("Invalid checkout request {0}", pathString);
        return statusHandler.handleRequest(request, response,
                new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
    }

    private boolean handleDelete(HttpServletRequest request, HttpServletResponse response, String pathString)
            throws IOException, JSONException, ServletException, URISyntaxException, CoreException,
            JGitInternalException, GitAPIException {
        IPath path = pathString == null ? Path.EMPTY : new Path(pathString);
        if (path.segment(0).equals("file") && path.segmentCount() > 1) { //$NON-NLS-1$

            // make sure a clone is addressed
            WebProject webProject = WebProject.fromId(path.segment(1));
            if (isAccessAllowed(request.getRemoteUser(), webProject)) {
                URI contentLocation = URI.create(webProject.getId());
                IPath projectPath = new Path(contentLocation.getPath()).append(path.removeFirstSegments(2));
                File gitDir = GitUtils.getGitDirs(new Path("file").append(projectPath), Traverse.CURRENT).values()
                        .iterator().next();

                Repository repo = new FileRepository(gitDir);
                repo.close();
                FileUtils.delete(repo.getWorkTree(), FileUtils.RECURSIVE | FileUtils.RETRY);
                if (path.segmentCount() == 2)
                    return statusHandler.handleRequest(request, response,
                            removeProject(request.getRemoteUser(), webProject));
                return true;
            } else {
                String msg = NLS.bind("Nothing found for the given ID: {0}", path);
                return statusHandler.handleRequest(request, response,
                        new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, null));
            }
        }
        String msg = NLS.bind("Invalid delete request {0}", pathString);
        return statusHandler.handleRequest(request, response,
                new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
    }

    /**
     * Returns whether the user can access the given project
     */
    private boolean isAccessAllowed(String userName, WebProject webProject) {
        try {
            WebUser webUser = WebUser.fromUserName(userName);
            JSONArray workspacesJSON = webUser.getWorkspacesJSON();
            for (int i = 0; i < workspacesJSON.length(); i++) {
                JSONObject workspace = workspacesJSON.getJSONObject(i);
                String workspaceId = workspace.getString(ProtocolConstants.KEY_ID);
                WebWorkspace webWorkspace = WebWorkspace.fromId(workspaceId);
                JSONArray projectsJSON = webWorkspace.getProjectsJSON();
                for (int j = 0; j < projectsJSON.length(); j++) {
                    JSONObject project = projectsJSON.getJSONObject(j);
                    String projectId = project.getString(ProtocolConstants.KEY_ID);
                    if (projectId.equals(webProject.getId()))
                        return true;
                }
            }
        } catch (JSONException e) {
            // ignore, deny access
        }
        return false;
    }

    /**
     * Looks for the project in all workspaces of the user and removes it when found.
     * 
     * @see WorkspaceResourceHandler#handleRemoveProject(HttpServletRequest, HttpServletResponse, WebWorkspace)
     * 
     * @param userName the user name
     * @param webProject the project to remove
     * @return ServerStatus <code>OK</code> if the project has been found and successfully removed,
     * <code>ERROR</code> if an error occurred or the project couldn't be found
     */
    static ServerStatus removeProject(String userName, WebProject webProject) {
        try {
            WebUser webUser = WebUser.fromUserName(userName);
            JSONArray workspacesJSON = webUser.getWorkspacesJSON();
            for (int i = 0; i < workspacesJSON.length(); i++) {
                JSONObject workspace = workspacesJSON.getJSONObject(i);
                String workspaceId = workspace.getString(ProtocolConstants.KEY_ID);
                WebWorkspace webWorkspace = WebWorkspace.fromId(workspaceId);
                JSONArray projectsJSON = webWorkspace.getProjectsJSON();
                for (int j = 0; j < projectsJSON.length(); j++) {
                    JSONObject project = projectsJSON.getJSONObject(j);
                    String projectId = project.getString(ProtocolConstants.KEY_ID);
                    if (projectId.equals(webProject.getId())) {

                        //If found, remove project from workspace
                        webWorkspace.removeProject(webProject);

                        // remove the project folder
                        try {
                            WorkspaceResourceHandler.removeProject(webProject, userName);
                        } catch (CoreException e) {
                            //we are unable to write in the platform location!
                            String msg = NLS.bind("Server content location could not be written: {0}",
                                    Activator.getDefault().getRootLocationURI());
                            return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                    msg, e);
                        }

                        //save the workspace and project metadata
                        try {
                            webProject.save();
                            webWorkspace.save();
                        } catch (CoreException e) {
                            String msg = "Error persisting project state";
                            return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                    msg, e);
                        }

                        return new ServerStatus(IStatus.OK, HttpServletResponse.SC_OK, null, null);
                    }
                }
            }
        } catch (JSONException e) {
            // ignore, no project will be harmed
        }
        // FIXME: not sure about this one
        return new ServerStatus(IStatus.OK, HttpServletResponse.SC_OK, null, null);
    }

    /**
     * Validates that the provided clone name is valid. Returns
     * <code>true</code> if the clone name is valid, and <code>false</code>
     * otherwise. This method takes care of setting the error response when the
     * clone name is not valid.
     */
    private boolean validateCloneName(String name, HttpServletRequest request, HttpServletResponse response)
            throws ServletException {
        // TODO: implement
        return true;
    }

    private boolean validateCloneUrl(String url, HttpServletRequest request, HttpServletResponse response)
            throws ServletException {
        if (url == null || url.trim().length() == 0) {
            statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_BAD_REQUEST, "Clone URL cannot be empty", null)); //$NON-NLS-1$
            return false;
        }
        try {
            new URIish(url);
        } catch (URISyntaxException e) {
            statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_BAD_REQUEST, NLS.bind("Invalid clone URL: {0}", url), e)); //$NON-NLS-1$
            return false;
        }
        return true;
    }

    private JSONObject toJSON(Entry<IPath, File> entry, URI baseLocation) throws URISyntaxException {
        IPath k = entry.getKey();
        JSONObject result = new JSONObject();
        try {
            result.put(ProtocolConstants.KEY_ID, k);

            result.put(ProtocolConstants.KEY_NAME,
                    k.segmentCount() == 1 ? WebProject.fromId(k.segment(0)).getName() : k.lastSegment());

            IPath np = new Path(GitServlet.GIT_URI).append(GitConstants.CLONE_RESOURCE).append("file").append(k); //$NON-NLS-1$
            URI location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(ProtocolConstants.KEY_LOCATION, location);

            np = new Path("file").append(k).makeAbsolute(); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(ProtocolConstants.KEY_CONTENT_LOCATION, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.REMOTE_RESOURCE).append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_REMOTE, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.CONFIG_RESOURCE)
                    .append(GitConstants.CLONE_RESOURCE).append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_CONFIG, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.COMMIT_RESOURCE).append(Constants.HEAD)
                    .append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_HEAD, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.COMMIT_RESOURCE).append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_COMMIT, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.BRANCH_RESOURCE).append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_BRANCH, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.INDEX_RESOURCE).append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_INDEX, location);

            np = new Path(GitServlet.GIT_URI).append(GitConstants.STATUS_RESOURCE).append("file").append(k); //$NON-NLS-1$
            location = new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(),
                    baseLocation.getPort(), np.toString(), baseLocation.getQuery(), baseLocation.getFragment());
            result.put(GitConstants.KEY_STATUS, location);

            try {
                FileBasedConfig config = new FileRepository(entry.getValue()).getConfig();
                String remoteUri = config.getString(ConfigConstants.CONFIG_REMOTE_SECTION,
                        Constants.DEFAULT_REMOTE_NAME, ConfigConstants.CONFIG_KEY_URL);
                if (remoteUri != null)
                    result.put(GitConstants.KEY_URL, remoteUri);
            } catch (IOException e) {
                // ignore and skip Git URL
            }
        } catch (JSONException e) {
            //cannot happen, we know keys and values are valid
        }
        return result;
    }

    /**
     * It's a copy @see WorkspaceResourceHandler#addProjectRights().
     * TODO: We can do better than that.
     *
     * @param request
     * @param response
     * @param location
     * @throws ServletException
     */
    private void addProjectRights(HttpServletRequest request, HttpServletResponse response, String location)
            throws ServletException {
        if (location == null)
            return;
        try {
            String locationPath = URI.create(location).getPath();
            //right to access the location
            AuthorizationService.addUserRight(request.getRemoteUser(), locationPath);
            //right to access all children of the location
            if (locationPath.endsWith("/")) //$NON-NLS-1$
                locationPath += "*"; //$NON-NLS-1$
            else
                locationPath += "/*"; //$NON-NLS-1$
            AuthorizationService.addUserRight(request.getRemoteUser(), locationPath);
        } catch (CoreException e) {
            statusHandler.handleRequest(request, response, e.getStatus());
        }
    }
}