blob: 2c93ba5c3cde12f2664f2a3a1164faa5441880ed [file] [log] [blame]
// Copyright (C) 2008 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.client.rpc;
import com.google.gerrit.client.data.ProjectCache;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountGroup;
import com.google.gerrit.client.reviewdb.ApprovalCategory;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.reviewdb.ProjectRight;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException;
import java.util.Set;
/** Support for services which require a {@link ReviewDb} instance. */
public class BaseServiceImplementation {
/**
* Executes <code>action.run</code> with an active ReviewDb connection.
* <p>
* A database handle is automatically opened and closed around the action's
* {@link Action#run(ReviewDb)} method. OrmExceptions are caught and passed
* into the onFailure method of the callback.
*
* @param <T> type of result the callback expects.
* @param callback the callback that will receive the result.
* @param action the action logic to perform.
*/
protected <T> void run(final AsyncCallback<T> callback, final Action<T> action) {
try {
final ReviewDb db = Common.getSchemaFactory().open();
final T r;
try {
r = action.run(db);
} finally {
db.close();
}
if (r != null) {
callback.onSuccess(r);
}
} catch (OrmException e) {
if (e.getCause() instanceof Failure) {
callback.onFailure(e.getCause().getCause());
} else if (e.getCause() instanceof CorruptEntityException) {
callback.onFailure(e.getCause());
} else if (e.getCause() instanceof NoSuchEntityException) {
callback.onFailure(e.getCause());
} else if (e.getCause() instanceof NoDifferencesException) {
callback.onFailure(e.getCause());
} else {
callback.onFailure(e);
}
} catch (Failure e) {
callback.onFailure(e.getCause());
}
}
/** Throws NoSuchEntityException if the caller cannot access the project. */
public static void assertCanRead(final Change change) throws Failure {
if (!canRead(change)) {
throw new Failure(new NoSuchEntityException());
}
}
/** Throws NoSuchEntityException if the caller cannot access the project. */
public static void assertCanRead(final Project.NameKey projectKey)
throws Failure {
if (!canRead(projectKey)) {
throw new Failure(new NoSuchEntityException());
}
}
/** Return true if the current user can read this change's project. */
public static boolean canRead(final Change change) {
return change != null && canRead(change.getDest().getParentKey());
}
/** Return true if the current user can read this project, and its contents. */
public static boolean canRead(final Project.NameKey projectKey) {
return canRead(Common.getAccountId(), projectKey);
}
public static boolean canRead(final Account.Id who,
final Project.NameKey projectKey) {
final ProjectCache.Entry e = Common.getProjectCache().get(projectKey);
if (e == null) {
// Unexpected, a project disappearing. But claim its not available.
//
return false;
}
final Set<AccountGroup.Id> myGroups = Common.getGroupCache().getEffectiveGroups(who);
return canPerform(myGroups, e, ApprovalCategory.READ, (short) 1, true);
}
public static boolean canPerform(final Set<AccountGroup.Id> myGroups,
final ProjectCache.Entry e, final ApprovalCategory.Id actionId,
final short requireValue, final boolean assumeOwner) {
if (assumeOwner && myGroups.contains(e.getProject().getOwnerGroupId())) {
// Ownership implies full access.
//
return true;
}
int val = Integer.MIN_VALUE;
for (final ProjectRight pr : e.getRights()) {
if (actionId.equals(pr.getApprovalCategoryId())
&& myGroups.contains(pr.getAccountGroupId())) {
if (val < 0 && pr.getMaxValue() > 0) {
// If one of the user's groups had denied them access, but
// this group grants them access, prefer the grant over
// the denial. We have to break the tie somehow and we
// prefer being "more open" to being "more closed".
//
val = pr.getMaxValue();
} else {
// Otherwise we use the largest value we can get.
//
val = Math.max(pr.getMaxValue(), val);
}
}
}
if (val == Integer.MIN_VALUE) {
for (final ProjectRight pr : Common.getProjectCache().getWildcardRights()) {
if (actionId.equals(pr.getApprovalCategoryId())
&& myGroups.contains(pr.getAccountGroupId())) {
val = Math.max(pr.getMaxValue(), val);
}
}
}
return val >= requireValue;
}
/** Exception whose cause is passed into onFailure. */
public static class Failure extends Exception {
public Failure(final Throwable why) {
super(why);
}
}
/** Arbitrary action to run with a database connection. */
public static interface Action<T> {
/**
* Perform this action, returning the onSuccess value.
*
* @param db an open database handle to be used by this connection.
* @return he value to pass to {@link AsyncCallback#onSuccess(Object)}.
* @throws OrmException any schema based action failed.
* @throws Failure cause is given to
* {@link AsyncCallback#onFailure(Throwable)}.
*/
T run(ReviewDb db) throws OrmException, Failure;
}
}