Line data Source code
1 : // Copyright (C) 2013 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.restapi.project; 16 : 17 : import com.google.common.base.Strings; 18 : import com.google.gerrit.entities.Project; 19 : import com.google.gerrit.entities.RefNames; 20 : import com.google.gerrit.extensions.api.projects.HeadInput; 21 : import com.google.gerrit.extensions.events.HeadUpdatedListener; 22 : import com.google.gerrit.extensions.restapi.AuthException; 23 : import com.google.gerrit.extensions.restapi.BadRequestException; 24 : import com.google.gerrit.extensions.restapi.ResourceNotFoundException; 25 : import com.google.gerrit.extensions.restapi.Response; 26 : import com.google.gerrit.extensions.restapi.RestModifyView; 27 : import com.google.gerrit.extensions.restapi.UnprocessableEntityException; 28 : import com.google.gerrit.git.LockFailureException; 29 : import com.google.gerrit.server.IdentifiedUser; 30 : import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent; 31 : import com.google.gerrit.server.git.GitRepositoryManager; 32 : import com.google.gerrit.server.permissions.PermissionBackend; 33 : import com.google.gerrit.server.permissions.PermissionBackendException; 34 : import com.google.gerrit.server.permissions.RefPermission; 35 : import com.google.gerrit.server.plugincontext.PluginSetContext; 36 : import com.google.gerrit.server.project.ProjectResource; 37 : import com.google.inject.Inject; 38 : import com.google.inject.Provider; 39 : import com.google.inject.Singleton; 40 : import java.io.IOException; 41 : import java.util.Map; 42 : import org.eclipse.jgit.errors.RepositoryNotFoundException; 43 : import org.eclipse.jgit.lib.Constants; 44 : import org.eclipse.jgit.lib.Ref; 45 : import org.eclipse.jgit.lib.RefUpdate; 46 : import org.eclipse.jgit.lib.Repository; 47 : 48 : @Singleton 49 : public class SetHead implements RestModifyView<ProjectResource, HeadInput> { 50 : private final GitRepositoryManager repoManager; 51 : private final Provider<IdentifiedUser> identifiedUser; 52 : private final PluginSetContext<HeadUpdatedListener> headUpdatedListeners; 53 : private final PermissionBackend permissionBackend; 54 : 55 : @Inject 56 : SetHead( 57 : GitRepositoryManager repoManager, 58 : Provider<IdentifiedUser> identifiedUser, 59 : PluginSetContext<HeadUpdatedListener> headUpdatedListeners, 60 146 : PermissionBackend permissionBackend) { 61 146 : this.repoManager = repoManager; 62 146 : this.identifiedUser = identifiedUser; 63 146 : this.headUpdatedListeners = headUpdatedListeners; 64 146 : this.permissionBackend = permissionBackend; 65 146 : } 66 : 67 : @Override 68 : public Response<String> apply(ProjectResource rsrc, HeadInput input) 69 : throws AuthException, ResourceNotFoundException, BadRequestException, 70 : UnprocessableEntityException, IOException, PermissionBackendException { 71 2 : if (input == null || Strings.isNullOrEmpty(input.ref)) { 72 1 : throw new BadRequestException("ref required"); 73 : } 74 1 : String ref = RefNames.fullName(input.ref); 75 : 76 1 : permissionBackend 77 1 : .user(rsrc.getUser()) 78 1 : .project(rsrc.getNameKey()) 79 1 : .ref(ref) 80 1 : .check(RefPermission.SET_HEAD); 81 : 82 1 : try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) { 83 1 : Map<String, Ref> cur = repo.getRefDatabase().exactRef(Constants.HEAD, ref); 84 1 : if (!cur.containsKey(ref)) { 85 1 : throw new UnprocessableEntityException(String.format("Ref Not Found: %s", ref)); 86 : } 87 : 88 1 : final String oldHead = cur.get(Constants.HEAD).getTarget().getName(); 89 1 : final String newHead = ref; 90 1 : if (!oldHead.equals(newHead)) { 91 1 : final RefUpdate u = repo.updateRef(Constants.HEAD, true); 92 1 : u.setRefLogIdent(identifiedUser.get().newRefLogIdent()); 93 1 : RefUpdate.Result res = u.link(newHead); 94 1 : switch (res) { 95 : case NO_CHANGE: 96 : case RENAMED: 97 : case FORCED: 98 : case NEW: 99 1 : break; 100 : case LOCK_FAILURE: 101 0 : throw new LockFailureException("Setting HEAD failed", u); 102 : case FAST_FORWARD: 103 : case IO_FAILURE: 104 : case NOT_ATTEMPTED: 105 : case REJECTED: 106 : case REJECTED_CURRENT_BRANCH: 107 : case REJECTED_MISSING_OBJECT: 108 : case REJECTED_OTHER_REASON: 109 : default: 110 0 : throw new IOException("Setting HEAD failed with " + res); 111 : } 112 : 113 1 : fire(rsrc.getNameKey(), oldHead, newHead); 114 : } 115 1 : return Response.ok(ref); 116 0 : } catch (RepositoryNotFoundException e) { 117 0 : throw new ResourceNotFoundException(rsrc.getName(), e); 118 : } 119 : } 120 : 121 : private void fire(Project.NameKey nameKey, String oldHead, String newHead) { 122 1 : if (headUpdatedListeners.isEmpty()) { 123 0 : return; 124 : } 125 : 126 1 : Event event = new Event(nameKey, oldHead, newHead); 127 1 : headUpdatedListeners.runEach(l -> l.onHeadUpdated(event)); 128 1 : } 129 : 130 : static class Event extends AbstractNoNotifyEvent implements HeadUpdatedListener.Event { 131 : private final Project.NameKey nameKey; 132 : private final String oldHead; 133 : private final String newHead; 134 : 135 1 : Event(Project.NameKey nameKey, String oldHead, String newHead) { 136 1 : this.nameKey = nameKey; 137 1 : this.oldHead = oldHead; 138 1 : this.newHead = newHead; 139 1 : } 140 : 141 : @Override 142 : public String getProjectName() { 143 1 : return nameKey.get(); 144 : } 145 : 146 : @Override 147 : public String getOldHeadName() { 148 1 : return oldHead; 149 : } 150 : 151 : @Override 152 : public String getNewHeadName() { 153 1 : return newHead; 154 : } 155 : } 156 : }