
This moves most Collections and all RestViews for {change,group,account,access} into server/restapi/ . These have to be done a single change and end up in a single java_library as these four groups use each other's code. Resources are left in place, to maintain compatibility for plugins. Some Json classes (ActionJson, ChangeJson, GroupJson) have many dependencies on the server library, so they are left in the server package. Revisisons (the Collection for RevisionResource) is left in place, as it is used in ActionJson This reduces the giant server package from ~1200 to ~900 files. Change-Id: Id0d9030ad2845d636875e6139a08243f8b624338
163 lines
5.7 KiB
Java
163 lines
5.7 KiB
Java
// Copyright (C) 2013 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package com.google.gerrit.server.restapi.project;
|
|
|
|
import com.google.common.base.Strings;
|
|
import com.google.gerrit.extensions.api.projects.HeadInput;
|
|
import com.google.gerrit.extensions.events.HeadUpdatedListener;
|
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
|
import com.google.gerrit.extensions.restapi.AuthException;
|
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
|
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
|
import com.google.gerrit.reviewdb.client.Project;
|
|
import com.google.gerrit.reviewdb.client.RefNames;
|
|
import com.google.gerrit.server.IdentifiedUser;
|
|
import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent;
|
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
|
import com.google.gerrit.server.permissions.RefPermission;
|
|
import com.google.gerrit.server.project.ProjectResource;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Provider;
|
|
import com.google.inject.Singleton;
|
|
import java.io.IOException;
|
|
import java.util.Map;
|
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
|
import org.eclipse.jgit.lib.Constants;
|
|
import org.eclipse.jgit.lib.Ref;
|
|
import org.eclipse.jgit.lib.RefUpdate;
|
|
import org.eclipse.jgit.lib.Repository;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
@Singleton
|
|
public class SetHead implements RestModifyView<ProjectResource, HeadInput> {
|
|
private static final Logger log = LoggerFactory.getLogger(SetHead.class);
|
|
|
|
private final GitRepositoryManager repoManager;
|
|
private final Provider<IdentifiedUser> identifiedUser;
|
|
private final DynamicSet<HeadUpdatedListener> headUpdatedListeners;
|
|
private final PermissionBackend permissionBackend;
|
|
|
|
@Inject
|
|
SetHead(
|
|
GitRepositoryManager repoManager,
|
|
Provider<IdentifiedUser> identifiedUser,
|
|
DynamicSet<HeadUpdatedListener> headUpdatedListeners,
|
|
PermissionBackend permissionBackend) {
|
|
this.repoManager = repoManager;
|
|
this.identifiedUser = identifiedUser;
|
|
this.headUpdatedListeners = headUpdatedListeners;
|
|
this.permissionBackend = permissionBackend;
|
|
}
|
|
|
|
@Override
|
|
public String apply(ProjectResource rsrc, HeadInput input)
|
|
throws AuthException, ResourceNotFoundException, BadRequestException,
|
|
UnprocessableEntityException, IOException, PermissionBackendException {
|
|
if (input == null || Strings.isNullOrEmpty(input.ref)) {
|
|
throw new BadRequestException("ref required");
|
|
}
|
|
String ref = RefNames.fullName(input.ref);
|
|
|
|
permissionBackend
|
|
.user(rsrc.getUser())
|
|
.project(rsrc.getNameKey())
|
|
.ref(ref)
|
|
.check(RefPermission.SET_HEAD);
|
|
|
|
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
|
|
Map<String, Ref> cur = repo.getRefDatabase().exactRef(Constants.HEAD, ref);
|
|
if (!cur.containsKey(ref)) {
|
|
throw new UnprocessableEntityException(String.format("Ref Not Found: %s", ref));
|
|
}
|
|
|
|
final String oldHead = cur.get(Constants.HEAD).getTarget().getName();
|
|
final String newHead = ref;
|
|
if (!oldHead.equals(newHead)) {
|
|
final RefUpdate u = repo.updateRef(Constants.HEAD, true);
|
|
u.setRefLogIdent(identifiedUser.get().newRefLogIdent());
|
|
RefUpdate.Result res = u.link(newHead);
|
|
switch (res) {
|
|
case NO_CHANGE:
|
|
case RENAMED:
|
|
case FORCED:
|
|
case NEW:
|
|
break;
|
|
case FAST_FORWARD:
|
|
case IO_FAILURE:
|
|
case LOCK_FAILURE:
|
|
case NOT_ATTEMPTED:
|
|
case REJECTED:
|
|
case REJECTED_CURRENT_BRANCH:
|
|
case REJECTED_MISSING_OBJECT:
|
|
case REJECTED_OTHER_REASON:
|
|
default:
|
|
throw new IOException("Setting HEAD failed with " + res);
|
|
}
|
|
|
|
fire(rsrc.getNameKey(), oldHead, newHead);
|
|
}
|
|
return ref;
|
|
} catch (RepositoryNotFoundException e) {
|
|
throw new ResourceNotFoundException(rsrc.getName());
|
|
}
|
|
}
|
|
|
|
private void fire(Project.NameKey nameKey, String oldHead, String newHead) {
|
|
if (!headUpdatedListeners.iterator().hasNext()) {
|
|
return;
|
|
}
|
|
Event event = new Event(nameKey, oldHead, newHead);
|
|
for (HeadUpdatedListener l : headUpdatedListeners) {
|
|
try {
|
|
l.onHeadUpdated(event);
|
|
} catch (RuntimeException e) {
|
|
log.warn("Failure in HeadUpdatedListener", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
static class Event extends AbstractNoNotifyEvent implements HeadUpdatedListener.Event {
|
|
private final Project.NameKey nameKey;
|
|
private final String oldHead;
|
|
private final String newHead;
|
|
|
|
Event(Project.NameKey nameKey, String oldHead, String newHead) {
|
|
this.nameKey = nameKey;
|
|
this.oldHead = oldHead;
|
|
this.newHead = newHead;
|
|
}
|
|
|
|
@Override
|
|
public String getProjectName() {
|
|
return nameKey.get();
|
|
}
|
|
|
|
@Override
|
|
public String getOldHeadName() {
|
|
return oldHead;
|
|
}
|
|
|
|
@Override
|
|
public String getNewHeadName() {
|
|
return newHead;
|
|
}
|
|
}
|
|
}
|