Dissolve gerrit-server top-level directory
Change-Id: I538512dfe0f1bea774c01fdd45fa410a45634011
This commit is contained in:
committed by
Dave Borowitz
parent
472396c797
commit
376a7bbb64
40
java/com/google/gerrit/server/util/CommitMessageUtil.java
Normal file
40
java/com/google/gerrit/server/util/CommitMessageUtil.java
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2017 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.util;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
|
||||
/** Utility functions to manipulate commit messages. */
|
||||
public class CommitMessageUtil {
|
||||
|
||||
private CommitMessageUtil() {}
|
||||
|
||||
/**
|
||||
* Checks for null or empty commit messages and appends a newline character to the commit message.
|
||||
*
|
||||
* @throws BadRequestException if the commit message is null or empty
|
||||
* @returns the trimmed message with a trailing newline character
|
||||
*/
|
||||
public static String checkAndSanitizeCommitMessage(String commitMessage)
|
||||
throws BadRequestException {
|
||||
String wellFormedMessage = Strings.nullToEmpty(commitMessage).trim();
|
||||
if (wellFormedMessage.isEmpty()) {
|
||||
throw new BadRequestException("Commit message cannot be null or empty");
|
||||
}
|
||||
wellFormedMessage = wellFormedMessage + "\n";
|
||||
return wellFormedMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2012 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.AnonymousUser;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
/**
|
||||
* The default RequestContext to use when not in a request scope e.g. ThreadLocalRequestContext is
|
||||
* not set.
|
||||
*/
|
||||
@Singleton
|
||||
public class FallbackRequestContext implements RequestContext {
|
||||
|
||||
private final AnonymousUser user;
|
||||
|
||||
@Inject
|
||||
FallbackRequestContext(AnonymousUser user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrentUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<ReviewDb> getReviewDbProvider() {
|
||||
return new Provider<ReviewDb>() {
|
||||
@Override
|
||||
public ReviewDb get() {
|
||||
throw new ProvisionException("Automatic ReviewDb only available in request scope");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2012 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.util;
|
||||
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.server.RemotePeer;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.servlet.ServletScopes;
|
||||
import com.google.inject.util.Providers;
|
||||
import com.google.inject.util.Types;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/** Propagator for Guice's built-in servlet scope. */
|
||||
public class GuiceRequestScopePropagator extends RequestScopePropagator {
|
||||
|
||||
private final String url;
|
||||
private final SocketAddress peer;
|
||||
|
||||
@Inject
|
||||
GuiceRequestScopePropagator(
|
||||
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
|
||||
@RemotePeer Provider<SocketAddress> remotePeerProvider,
|
||||
ThreadLocalRequestContext local,
|
||||
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
|
||||
super(ServletScopes.REQUEST, local, dbProviderProvider);
|
||||
this.url = urlProvider != null ? urlProvider.get() : null;
|
||||
this.peer = remotePeerProvider.get();
|
||||
}
|
||||
|
||||
/** @see RequestScopePropagator#wrap(Callable) */
|
||||
// ServletScopes#continueRequest is deprecated, but it's not obvious their
|
||||
// recommended replacement is an appropriate drop-in solution; see
|
||||
// https://gerrit-review.googlesource.com/83971
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected <T> Callable<T> wrapImpl(Callable<T> callable) {
|
||||
Map<Key<?>, Object> seedMap = new HashMap<>();
|
||||
|
||||
// Request scopes appear to use specific keys in their map, instead of only
|
||||
// providers. Add bindings for both the key to the instance directly and the
|
||||
// provider to the instance to be safe.
|
||||
seedMap.put(Key.get(typeOfProvider(String.class), CanonicalWebUrl.class), Providers.of(url));
|
||||
seedMap.put(Key.get(String.class, CanonicalWebUrl.class), url);
|
||||
|
||||
seedMap.put(Key.get(typeOfProvider(SocketAddress.class), RemotePeer.class), Providers.of(peer));
|
||||
seedMap.put(Key.get(SocketAddress.class, RemotePeer.class), peer);
|
||||
|
||||
return ServletScopes.continueRequest(callable, seedMap);
|
||||
}
|
||||
|
||||
private ParameterizedType typeOfProvider(Type type) {
|
||||
return Types.newParameterizedType(Provider.class, type);
|
||||
}
|
||||
}
|
||||
46
java/com/google/gerrit/server/util/HostPlatform.java
Normal file
46
java/com/google/gerrit/server/util/HostPlatform.java
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2009 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.util;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
public final class HostPlatform {
|
||||
private static final boolean win32 = compute("windows");
|
||||
private static final boolean mac = compute("mac");
|
||||
|
||||
/** @return true if this JVM is running on a Windows platform. */
|
||||
public static boolean isWin32() {
|
||||
return win32;
|
||||
}
|
||||
|
||||
public static boolean isMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
private static boolean compute(String platform) {
|
||||
final String osDotName =
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
return System.getProperty("os.name");
|
||||
}
|
||||
});
|
||||
return osDotName != null && osDotName.toLowerCase().contains(platform);
|
||||
}
|
||||
|
||||
private HostPlatform() {}
|
||||
}
|
||||
99
java/com/google/gerrit/server/util/IdGenerator.java
Normal file
99
java/com/google/gerrit/server/util/IdGenerator.java
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2009 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.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** Simple class to produce 4 billion keys randomly distributed. */
|
||||
@Singleton
|
||||
public class IdGenerator {
|
||||
/** Format an id created by this class as a hex string. */
|
||||
public static String format(int id) {
|
||||
final char[] r = new char[8];
|
||||
for (int p = 7; 0 <= p; p--) {
|
||||
final int h = id & 0xf;
|
||||
r[p] = h < 10 ? (char) ('0' + h) : (char) ('a' + (h - 10));
|
||||
id >>= 4;
|
||||
}
|
||||
return new String(r);
|
||||
}
|
||||
|
||||
private final AtomicInteger gen;
|
||||
|
||||
@Inject
|
||||
IdGenerator() {
|
||||
gen = new AtomicInteger(new Random().nextInt());
|
||||
}
|
||||
|
||||
/** Produce the next identifier. */
|
||||
public int next() {
|
||||
return mix(gen.getAndIncrement());
|
||||
}
|
||||
|
||||
private static final int salt = 0x9e3779b9;
|
||||
|
||||
static int mix(int in) {
|
||||
return mix(salt, in);
|
||||
}
|
||||
|
||||
/** A very simple bit permutation to mask a simple incrementer. */
|
||||
public static int mix(int salt, int in) {
|
||||
short v0 = hi16(in);
|
||||
short v1 = lo16(in);
|
||||
v0 += ((v1 << 2) + 0 ^ v1) + (salt ^ (v1 >>> 3)) + 1;
|
||||
v1 += ((v0 << 2) + 2 ^ v0) + (salt ^ (v0 >>> 3)) + 3;
|
||||
return result(v0, v1);
|
||||
}
|
||||
|
||||
/* For testing only. */
|
||||
static int unmix(int in) {
|
||||
short v0 = hi16(in);
|
||||
short v1 = lo16(in);
|
||||
v1 -= ((v0 << 2) + 2 ^ v0) + (salt ^ (v0 >>> 3)) + 3;
|
||||
v0 -= ((v1 << 2) + 0 ^ v1) + (salt ^ (v1 >>> 3)) + 1;
|
||||
return result(v0, v1);
|
||||
}
|
||||
|
||||
private static short hi16(int in) {
|
||||
return (short)
|
||||
( //
|
||||
((in >>> 24 & 0xff))
|
||||
| //
|
||||
((in >>> 16 & 0xff) << 8) //
|
||||
);
|
||||
}
|
||||
|
||||
private static short lo16(int in) {
|
||||
return (short)
|
||||
( //
|
||||
((in >>> 8 & 0xff))
|
||||
| //
|
||||
((in & 0xff) << 8) //
|
||||
);
|
||||
}
|
||||
|
||||
private static int result(short v0, short v1) {
|
||||
return ((v0 & 0xff) << 24)
|
||||
| //
|
||||
(((v0 >>> 8) & 0xff) << 16)
|
||||
| //
|
||||
((v1 & 0xff) << 8)
|
||||
| //
|
||||
((v1 >>> 8) & 0xff);
|
||||
}
|
||||
}
|
||||
91
java/com/google/gerrit/server/util/LabelVote.java
Normal file
91
java/com/google/gerrit/server/util/LabelVote.java
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.data.LabelType;
|
||||
|
||||
/** A single vote on a label, consisting of a label name and a value. */
|
||||
@AutoValue
|
||||
public abstract class LabelVote {
|
||||
public static LabelVote parse(String text) {
|
||||
checkArgument(!Strings.isNullOrEmpty(text), "Empty label vote");
|
||||
if (text.charAt(0) == '-') {
|
||||
return create(text.substring(1), (short) 0);
|
||||
}
|
||||
short sign = 0;
|
||||
int i;
|
||||
for (i = text.length() - 1; i >= 0; i--) {
|
||||
int c = text.charAt(i);
|
||||
if (c == '-') {
|
||||
sign = (short) -1;
|
||||
break;
|
||||
} else if (c == '+') {
|
||||
sign = (short) 1;
|
||||
break;
|
||||
} else if (!('0' <= c && c <= '9')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sign == 0) {
|
||||
return create(text, (short) 1);
|
||||
}
|
||||
return create(text.substring(0, i), (short) (sign * Short.parseShort(text.substring(i + 1))));
|
||||
}
|
||||
|
||||
public static LabelVote parseWithEquals(String text) {
|
||||
checkArgument(!Strings.isNullOrEmpty(text), "Empty label vote");
|
||||
int e = text.lastIndexOf('=');
|
||||
checkArgument(e >= 0, "Label vote missing '=': %s", text);
|
||||
return create(text.substring(0, e), Short.parseShort(text.substring(e + 1)));
|
||||
}
|
||||
|
||||
public static StringBuilder appendTo(StringBuilder sb, String label, short value) {
|
||||
if (value == (short) 0) {
|
||||
return sb.append('-').append(label);
|
||||
} else if (value < 0) {
|
||||
return sb.append(label).append(value);
|
||||
}
|
||||
return sb.append(label).append('+').append(value);
|
||||
}
|
||||
|
||||
public static LabelVote create(String label, short value) {
|
||||
return new AutoValue_LabelVote(LabelType.checkNameInternal(label), value);
|
||||
}
|
||||
|
||||
public abstract String label();
|
||||
|
||||
public abstract short value();
|
||||
|
||||
public String format() {
|
||||
// Max short string length is "-32768".length() == 6.
|
||||
return appendTo(new StringBuilder(label().length() + 6), label(), value()).toString();
|
||||
}
|
||||
|
||||
public String formatWithEquals() {
|
||||
if (value() <= (short) 0) {
|
||||
return label() + '=' + value();
|
||||
}
|
||||
return label() + "=+" + value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format();
|
||||
}
|
||||
}
|
||||
115
java/com/google/gerrit/server/util/MagicBranch.java
Normal file
115
java/com/google/gerrit/server/util/MagicBranch.java
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2011 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.util;
|
||||
|
||||
import com.google.gerrit.common.data.Capable;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class MagicBranch {
|
||||
private static final Logger log = LoggerFactory.getLogger(MagicBranch.class);
|
||||
|
||||
public static final String NEW_CHANGE = "refs/for/";
|
||||
// TODO(xchangcheng): remove after 'repo' supports private/wip changes.
|
||||
public static final String NEW_DRAFT_CHANGE = "refs/drafts/";
|
||||
// TODO(xchangcheng): remove after migrating tools which are using this magic branch.
|
||||
public static final String NEW_PUBLISH_CHANGE = "refs/publish/";
|
||||
|
||||
/** Extracts the destination from a ref name */
|
||||
public static String getDestBranchName(String refName) {
|
||||
String magicBranch = NEW_CHANGE;
|
||||
if (refName.startsWith(NEW_DRAFT_CHANGE)) {
|
||||
magicBranch = NEW_DRAFT_CHANGE;
|
||||
} else if (refName.startsWith(NEW_PUBLISH_CHANGE)) {
|
||||
magicBranch = NEW_PUBLISH_CHANGE;
|
||||
}
|
||||
return refName.substring(magicBranch.length());
|
||||
}
|
||||
|
||||
/** Checks if the supplied ref name is a magic branch */
|
||||
public static boolean isMagicBranch(String refName) {
|
||||
return refName.startsWith(NEW_DRAFT_CHANGE)
|
||||
|| refName.startsWith(NEW_PUBLISH_CHANGE)
|
||||
|| refName.startsWith(NEW_CHANGE);
|
||||
}
|
||||
|
||||
/** Returns the ref name prefix for a magic branch, {@code null} if the branch is not magic */
|
||||
public static String getMagicRefNamePrefix(String refName) {
|
||||
if (refName.startsWith(NEW_DRAFT_CHANGE)) {
|
||||
return NEW_DRAFT_CHANGE;
|
||||
}
|
||||
if (refName.startsWith(NEW_PUBLISH_CHANGE)) {
|
||||
return NEW_PUBLISH_CHANGE;
|
||||
}
|
||||
if (refName.startsWith(NEW_CHANGE)) {
|
||||
return NEW_CHANGE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a (magic branch)/branch_name reference exists in the destination repository and only
|
||||
* returns Capable.OK if it does not match any.
|
||||
*
|
||||
* <p>These block the client from being able to even send us a pack file, as it is very unlikely
|
||||
* the user passed the --force flag and the new commit is probably not going to fast-forward the
|
||||
* branch.
|
||||
*/
|
||||
public static Capable checkMagicBranchRefs(Repository repo, Project project) {
|
||||
Capable result = checkMagicBranchRef(NEW_CHANGE, repo, project);
|
||||
if (result != Capable.OK) {
|
||||
return result;
|
||||
}
|
||||
result = checkMagicBranchRef(NEW_DRAFT_CHANGE, repo, project);
|
||||
if (result != Capable.OK) {
|
||||
return result;
|
||||
}
|
||||
result = checkMagicBranchRef(NEW_PUBLISH_CHANGE, repo, project);
|
||||
if (result != Capable.OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return Capable.OK;
|
||||
}
|
||||
|
||||
private static Capable checkMagicBranchRef(String branchName, Repository repo, Project project) {
|
||||
Map<String, Ref> blockingFors;
|
||||
try {
|
||||
blockingFors = repo.getRefDatabase().getRefs(branchName);
|
||||
} catch (IOException err) {
|
||||
String projName = project.getName();
|
||||
log.warn("Cannot scan refs in '" + projName + "'", err);
|
||||
return new Capable("Server process cannot read '" + projName + "'");
|
||||
}
|
||||
if (!blockingFors.isEmpty()) {
|
||||
String projName = project.getName();
|
||||
log.error(
|
||||
"Repository '"
|
||||
+ projName
|
||||
+ "' needs the following refs removed to receive changes: "
|
||||
+ blockingFors.keySet());
|
||||
return new Capable("One or more " + branchName + " names blocks change upload");
|
||||
}
|
||||
|
||||
return Capable.OK;
|
||||
}
|
||||
|
||||
private MagicBranch() {}
|
||||
}
|
||||
65
java/com/google/gerrit/server/util/ManualRequestContext.java
Normal file
65
java/com/google/gerrit/server/util/ManualRequestContext.java
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2015 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
/** Closeable version of a {@link RequestContext} with manually-specified providers. */
|
||||
public class ManualRequestContext implements RequestContext, AutoCloseable {
|
||||
private final Provider<CurrentUser> userProvider;
|
||||
private final Provider<ReviewDb> db;
|
||||
private final ThreadLocalRequestContext requestContext;
|
||||
private final RequestContext old;
|
||||
|
||||
public ManualRequestContext(
|
||||
CurrentUser user,
|
||||
SchemaFactory<ReviewDb> schemaFactory,
|
||||
ThreadLocalRequestContext requestContext)
|
||||
throws OrmException {
|
||||
this(Providers.of(user), schemaFactory, requestContext);
|
||||
}
|
||||
|
||||
public ManualRequestContext(
|
||||
Provider<CurrentUser> userProvider,
|
||||
SchemaFactory<ReviewDb> schemaFactory,
|
||||
ThreadLocalRequestContext requestContext)
|
||||
throws OrmException {
|
||||
this.userProvider = userProvider;
|
||||
this.db = Providers.of(schemaFactory.open());
|
||||
this.requestContext = requestContext;
|
||||
old = requestContext.setContext(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrentUser getUser() {
|
||||
return userProvider.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<ReviewDb> getReviewDbProvider() {
|
||||
return db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
requestContext.setContext(old);
|
||||
db.get().close();
|
||||
}
|
||||
}
|
||||
118
java/com/google/gerrit/server/util/MostSpecificComparator.java
Normal file
118
java/com/google/gerrit/server/util/MostSpecificComparator.java
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (C) 2011 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.util;
|
||||
|
||||
import com.google.gerrit.common.data.RefConfigSection;
|
||||
import com.google.gerrit.server.project.RefPattern;
|
||||
import java.util.Comparator;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* Order the Ref Pattern by the most specific. This sort is done by:
|
||||
*
|
||||
* <ul>
|
||||
* <li>1 - The minor value of Levenshtein string distance between the branch name and the regex
|
||||
* string shortest example. A shorter distance is a more specific match.
|
||||
* <li>2 - Finites first, infinities after.
|
||||
* <li>3 - Number of transitions. More transitions is more specific.
|
||||
* <li>4 - Length of the expression text.
|
||||
* </ul>
|
||||
*
|
||||
* Levenshtein distance is a measure of the similarity between two strings. The distance is the
|
||||
* number of deletions, insertions, or substitutions required to transform one string into another.
|
||||
*
|
||||
* <p>For example, if given refs/heads/m* and refs/heads/*, the distances are 5 and 6. It means that
|
||||
* refs/heads/m* is more specific because it's closer to refs/heads/master than refs/heads/*.
|
||||
*
|
||||
* <p>Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the distances are both 6. Both
|
||||
* are infinite, but refs/heads/[a-zA-Z]* has more transitions, which after all turns it more
|
||||
* specific.
|
||||
*/
|
||||
public final class MostSpecificComparator implements Comparator<RefConfigSection> {
|
||||
private final String refName;
|
||||
|
||||
public MostSpecificComparator(String refName) {
|
||||
this.refName = refName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(RefConfigSection a, RefConfigSection b) {
|
||||
return compare(a.getName(), b.getName());
|
||||
}
|
||||
|
||||
public int compare(String pattern1, String pattern2) {
|
||||
int cmp = distance(pattern1) - distance(pattern2);
|
||||
if (cmp == 0) {
|
||||
boolean p1_finite = finite(pattern1);
|
||||
boolean p2_finite = finite(pattern2);
|
||||
|
||||
if (p1_finite && !p2_finite) {
|
||||
cmp = -1;
|
||||
} else if (!p1_finite && p2_finite) {
|
||||
cmp = 1;
|
||||
} else /* if (f1 == f2) */ {
|
||||
cmp = 0;
|
||||
}
|
||||
}
|
||||
if (cmp == 0) {
|
||||
cmp = transitions(pattern2) - transitions(pattern1);
|
||||
}
|
||||
if (cmp == 0) {
|
||||
cmp = pattern2.length() - pattern1.length();
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
private int distance(String pattern) {
|
||||
String example;
|
||||
if (RefPattern.isRE(pattern)) {
|
||||
example = RefPattern.shortestExample(pattern);
|
||||
|
||||
} else if (pattern.endsWith("/*")) {
|
||||
example = pattern;
|
||||
|
||||
} else if (pattern.equals(refName)) {
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
return Math.max(pattern.length(), refName.length());
|
||||
}
|
||||
return StringUtils.getLevenshteinDistance(example, refName);
|
||||
}
|
||||
|
||||
private boolean finite(String pattern) {
|
||||
if (RefPattern.isRE(pattern)) {
|
||||
return RefPattern.toRegExp(pattern).toAutomaton().isFinite();
|
||||
|
||||
} else if (pattern.endsWith("/*")) {
|
||||
return false;
|
||||
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private int transitions(String pattern) {
|
||||
if (RefPattern.isRE(pattern)) {
|
||||
return RefPattern.toRegExp(pattern).toAutomaton().getNumberOfTransitions();
|
||||
|
||||
} else if (pattern.endsWith("/*")) {
|
||||
return pattern.length();
|
||||
|
||||
} else {
|
||||
return pattern.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
62
java/com/google/gerrit/server/util/OneOffRequestContext.java
Normal file
62
java/com/google/gerrit/server/util/OneOffRequestContext.java
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2015 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.InternalUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Helper to create one-off request contexts.
|
||||
*
|
||||
* <p>Each call to {@link #open()} opens a new {@link ReviewDb}, so this class should only be used
|
||||
* in a bounded try/finally block.
|
||||
*
|
||||
* <p>The user in the request context is {@link InternalUser} or the {@link IdentifiedUser}
|
||||
* associated to the userId passed as parameter.
|
||||
*/
|
||||
@Singleton
|
||||
public class OneOffRequestContext {
|
||||
private final InternalUser.Factory userFactory;
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final ThreadLocalRequestContext requestContext;
|
||||
private final IdentifiedUser.GenericFactory identifiedUserFactory;
|
||||
|
||||
@Inject
|
||||
OneOffRequestContext(
|
||||
InternalUser.Factory userFactory,
|
||||
SchemaFactory<ReviewDb> schemaFactory,
|
||||
ThreadLocalRequestContext requestContext,
|
||||
IdentifiedUser.GenericFactory identifiedUserFactory) {
|
||||
this.userFactory = userFactory;
|
||||
this.schemaFactory = schemaFactory;
|
||||
this.requestContext = requestContext;
|
||||
this.identifiedUserFactory = identifiedUserFactory;
|
||||
}
|
||||
|
||||
public ManualRequestContext open() throws OrmException {
|
||||
return new ManualRequestContext(userFactory.create(), schemaFactory, requestContext);
|
||||
}
|
||||
|
||||
public ManualRequestContext openAs(Account.Id userId) throws OrmException {
|
||||
return new ManualRequestContext(
|
||||
identifiedUserFactory.create(userId), schemaFactory, requestContext);
|
||||
}
|
||||
}
|
||||
59
java/com/google/gerrit/server/util/PluginLogFile.java
Normal file
59
java/com/google/gerrit/server/util/PluginLogFile.java
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2015 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.util;
|
||||
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.extensions.systemstatus.ServerInformation;
|
||||
import org.apache.log4j.AsyncAppender;
|
||||
import org.apache.log4j.Layout;
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public abstract class PluginLogFile implements LifecycleListener {
|
||||
|
||||
private final SystemLog systemLog;
|
||||
private final ServerInformation serverInfo;
|
||||
private final String logName;
|
||||
private final Layout layout;
|
||||
|
||||
public PluginLogFile(
|
||||
SystemLog systemLog, ServerInformation serverInfo, String logName, Layout layout) {
|
||||
this.systemLog = systemLog;
|
||||
this.serverInfo = serverInfo;
|
||||
this.logName = logName;
|
||||
this.layout = layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
AsyncAppender asyncAppender = systemLog.createAsyncAppender(logName, layout);
|
||||
Logger logger = LogManager.getLogger(logName);
|
||||
logger.removeAppender(logName);
|
||||
logger.addAppender(asyncAppender);
|
||||
logger.setAdditivity(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// stop is called when plugin is unloaded or when the server shutdown.
|
||||
// Only clean up when the server is shutting down to prevent issue when a
|
||||
// plugin is reloaded. The issue is that gerrit load the new plugin and then
|
||||
// unload the old one so because loggers are static, the unload of the old
|
||||
// plugin would remove the appenders just created by the new plugin.
|
||||
if (serverInfo.getState() == ServerInformation.State.SHUTDOWN) {
|
||||
LogManager.getLogger(logName).removeAllAppenders();
|
||||
}
|
||||
}
|
||||
}
|
||||
45
java/com/google/gerrit/server/util/PluginRequestContext.java
Normal file
45
java/com/google/gerrit/server/util/PluginRequestContext.java
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.PluginUser;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
|
||||
/** RequestContext active while plugins load or unload. */
|
||||
public class PluginRequestContext implements RequestContext {
|
||||
private final PluginUser user;
|
||||
|
||||
public PluginRequestContext(PluginUser user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrentUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<ReviewDb> getReviewDbProvider() {
|
||||
return new Provider<ReviewDb>() {
|
||||
@Override
|
||||
public ReviewDb get() {
|
||||
throw new ProvisionException("Automatic ReviewDb only available in request scope");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
103
java/com/google/gerrit/server/util/RegexListSearcher.java
Normal file
103
java/com/google/gerrit/server/util/RegexListSearcher.java
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2014 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.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Chars;
|
||||
import dk.brics.automaton.Automaton;
|
||||
import dk.brics.automaton.RegExp;
|
||||
import dk.brics.automaton.RunAutomaton;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Helper to search sorted lists for elements matching a regex. */
|
||||
public abstract class RegexListSearcher<T> implements Function<T, String> {
|
||||
public static RegexListSearcher<String> ofStrings(String re) {
|
||||
return new RegexListSearcher<String>(re) {
|
||||
@Override
|
||||
public String apply(String in) {
|
||||
return in;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final RunAutomaton pattern;
|
||||
|
||||
private final String prefixBegin;
|
||||
private final String prefixEnd;
|
||||
private final int prefixLen;
|
||||
private final boolean prefixOnly;
|
||||
|
||||
public RegexListSearcher(String re) {
|
||||
if (re.startsWith("^")) {
|
||||
re = re.substring(1);
|
||||
}
|
||||
|
||||
if (re.endsWith("$") && !re.endsWith("\\$")) {
|
||||
re = re.substring(0, re.length() - 1);
|
||||
}
|
||||
|
||||
Automaton automaton = new RegExp(re).toAutomaton();
|
||||
prefixBegin = automaton.getCommonPrefix();
|
||||
prefixLen = prefixBegin.length();
|
||||
|
||||
if (0 < prefixLen) {
|
||||
char max = Chars.checkedCast(prefixBegin.charAt(prefixLen - 1) + 1);
|
||||
prefixEnd = prefixBegin.substring(0, prefixLen - 1) + max;
|
||||
prefixOnly = re.equals(prefixBegin + ".*");
|
||||
} else {
|
||||
prefixEnd = "";
|
||||
prefixOnly = false;
|
||||
}
|
||||
|
||||
pattern = prefixOnly ? null : new RunAutomaton(automaton);
|
||||
}
|
||||
|
||||
public Iterable<T> search(List<T> list) {
|
||||
checkNotNull(list);
|
||||
int begin;
|
||||
int end;
|
||||
|
||||
if (0 < prefixLen) {
|
||||
// Assumes many consecutive elements may have the same prefix, so the cost
|
||||
// of two binary searches is less than iterating to find the endpoints.
|
||||
begin = find(list, prefixBegin);
|
||||
end = find(list, prefixEnd);
|
||||
} else {
|
||||
begin = 0;
|
||||
end = list.size();
|
||||
}
|
||||
|
||||
if (prefixOnly) {
|
||||
return begin < end ? list.subList(begin, end) : ImmutableList.<T>of();
|
||||
}
|
||||
|
||||
return Iterables.filter(list.subList(begin, end), x -> pattern.run(apply(x)));
|
||||
}
|
||||
|
||||
public boolean hasMatch(List<T> list) {
|
||||
return !Iterables.isEmpty(search(list));
|
||||
}
|
||||
|
||||
private int find(List<T> list, String p) {
|
||||
int r = Collections.binarySearch(Lists.transform(list, this), p);
|
||||
return r < 0 ? -(r + 1) : r;
|
||||
}
|
||||
}
|
||||
29
java/com/google/gerrit/server/util/RequestContext.java
Normal file
29
java/com/google/gerrit/server/util/RequestContext.java
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2012 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
* The RequestContext is an interface exposing the fields that are needed by the GerritGlobalModule
|
||||
* scope.
|
||||
*/
|
||||
public interface RequestContext {
|
||||
CurrentUser getUser();
|
||||
|
||||
Provider<ReviewDb> getReviewDbProvider();
|
||||
}
|
||||
70
java/com/google/gerrit/server/util/RequestId.java
Normal file
70
java/com/google/gerrit/server/util/RequestId.java
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (C) 2016 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.util;
|
||||
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/** Unique identifier for an end-user request, used in logs and similar. */
|
||||
public class RequestId {
|
||||
private static final String MACHINE_ID;
|
||||
|
||||
static {
|
||||
String id;
|
||||
try {
|
||||
id = InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
id = "unknown";
|
||||
}
|
||||
MACHINE_ID = id;
|
||||
}
|
||||
|
||||
public static RequestId forChange(Change c) {
|
||||
return new RequestId(c.getId().toString());
|
||||
}
|
||||
|
||||
public static RequestId forProject(Project.NameKey p) {
|
||||
return new RequestId(p.toString());
|
||||
}
|
||||
|
||||
private final String str;
|
||||
|
||||
private RequestId(String resourceId) {
|
||||
Hasher h = Hashing.murmur3_128().newHasher();
|
||||
h.putLong(Thread.currentThread().getId()).putUnencodedChars(MACHINE_ID);
|
||||
str =
|
||||
"["
|
||||
+ resourceId
|
||||
+ "-"
|
||||
+ TimeUtil.nowTs().getTime()
|
||||
+ "-"
|
||||
+ h.hash().toString().substring(0, 8)
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return str;
|
||||
}
|
||||
|
||||
public String toStringForStorage() {
|
||||
return str.substring(1, str.length() - 1);
|
||||
}
|
||||
}
|
||||
218
java/com/google/gerrit/server/util/RequestScopePropagator.java
Normal file
218
java/com/google/gerrit/server/util/RequestScopePropagator.java
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright (C) 2012 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.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.RequestCleanup;
|
||||
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
|
||||
import com.google.gerrit.server.git.ProjectRunnable;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Scope;
|
||||
import com.google.inject.servlet.ServletScopes;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* Base class for propagating request-scoped data between threads.
|
||||
*
|
||||
* <p>Request scopes are typically linked to a {@link ThreadLocal}, which is only available to the
|
||||
* current thread. In order to allow background work involving RequestScoped data, the ThreadLocal
|
||||
* data must be copied from the request thread to the new background thread.
|
||||
*
|
||||
* <p>Every type of RequestScope must provide an implementation of RequestScopePropagator. See
|
||||
* {@link #wrap(Callable)} for details on the implementation, usage, and restrictions.
|
||||
*
|
||||
* @see ThreadLocalRequestScopePropagator
|
||||
*/
|
||||
public abstract class RequestScopePropagator {
|
||||
|
||||
private final Scope scope;
|
||||
private final ThreadLocalRequestContext local;
|
||||
private final Provider<RequestScopedReviewDbProvider> dbProviderProvider;
|
||||
|
||||
protected RequestScopePropagator(
|
||||
Scope scope,
|
||||
ThreadLocalRequestContext local,
|
||||
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
|
||||
this.scope = scope;
|
||||
this.local = local;
|
||||
this.dbProviderProvider = dbProviderProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the current request state is available when the passed in Callable is invoked.
|
||||
*
|
||||
* <p>If needed wraps the passed in Callable in a new {@link Callable} that propagates the current
|
||||
* request state when the returned Callable is invoked. The method must be called in a request
|
||||
* scope and the returned Callable may only be invoked in a thread that is not already in a
|
||||
* request scope or is in the same request scope. The returned Callable will inherit toString()
|
||||
* from the passed in Callable. A {@link ScheduledThreadPoolExecutor} does not accept a Callable,
|
||||
* so there is no ProjectCallable implementation. Implementations of this method must be
|
||||
* consistent with Guice's {@link ServletScopes#continueRequest(Callable, java.util.Map)}.
|
||||
*
|
||||
* <p>There are some limitations:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Derived objects (i.e. anything marked created in a request scope) will not be
|
||||
* transported.
|
||||
* <li>State changes to the request scoped context after this method is called will not be seen
|
||||
* in the continued thread.
|
||||
* </ul>
|
||||
*
|
||||
* @param callable the Callable to wrap.
|
||||
* @return a new Callable which will execute in the current request scope.
|
||||
*/
|
||||
@SuppressWarnings("javadoc") // See GuiceRequestScopePropagator#wrapImpl
|
||||
public final <T> Callable<T> wrap(Callable<T> callable) {
|
||||
final RequestContext callerContext = checkNotNull(local.getContext());
|
||||
final Callable<T> wrapped = wrapImpl(context(callerContext, cleanup(callable)));
|
||||
return new Callable<T>() {
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
if (callerContext == local.getContext()) {
|
||||
return callable.call();
|
||||
}
|
||||
return wrapped.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return callable.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps runnable in a new {@link Runnable} that propagates the current request state when the
|
||||
* runnable is invoked. The method must be called in a request scope and the returned Runnable may
|
||||
* only be invoked in a thread that is not already in a request scope. The returned Runnable will
|
||||
* inherit toString() from the passed in Runnable. Furthermore, if the passed runnable is of type
|
||||
* {@link ProjectRunnable}, the returned runnable will be of the same type with the methods
|
||||
* delegated.
|
||||
*
|
||||
* <p>See {@link #wrap(Callable)} for details on implementation and usage.
|
||||
*
|
||||
* @param runnable the Runnable to wrap.
|
||||
* @return a new Runnable which will execute in the current request scope.
|
||||
*/
|
||||
public final Runnable wrap(Runnable runnable) {
|
||||
final Callable<Object> wrapped = wrap(Executors.callable(runnable));
|
||||
|
||||
if (runnable instanceof ProjectRunnable) {
|
||||
return new ProjectRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
wrapped.call();
|
||||
} catch (Exception e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e); // Not possible.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project.NameKey getProjectNameKey() {
|
||||
return ((ProjectRunnable) runnable).getProjectNameKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteName() {
|
||||
return ((ProjectRunnable) runnable).getRemoteName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomizedPrint() {
|
||||
return ((ProjectRunnable) runnable).hasCustomizedPrint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return runnable.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
wrapped.call();
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e); // Not possible.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return runnable.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @see #wrap(Callable) */
|
||||
protected abstract <T> Callable<T> wrapImpl(Callable<T> callable);
|
||||
|
||||
protected <T> Callable<T> context(RequestContext context, Callable<T> callable) {
|
||||
return () -> {
|
||||
RequestContext old =
|
||||
local.setContext(
|
||||
new RequestContext() {
|
||||
@Override
|
||||
public CurrentUser getUser() {
|
||||
return context.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<ReviewDb> getReviewDbProvider() {
|
||||
return dbProviderProvider.get();
|
||||
}
|
||||
});
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
local.setContext(old);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected <T> Callable<T> cleanup(Callable<T> callable) {
|
||||
return () -> {
|
||||
RequestCleanup cleanup =
|
||||
scope
|
||||
.scope(
|
||||
Key.get(RequestCleanup.class),
|
||||
new Provider<RequestCleanup>() {
|
||||
@Override
|
||||
public RequestCleanup get() {
|
||||
return new RequestCleanup();
|
||||
}
|
||||
})
|
||||
.get();
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
cleanup.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
47
java/com/google/gerrit/server/util/ServerRequestContext.java
Normal file
47
java/com/google/gerrit/server/util/ServerRequestContext.java
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.InternalUser;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
|
||||
/** RequestContext with an InternalUser making the internals visible. */
|
||||
public class ServerRequestContext implements RequestContext {
|
||||
private final InternalUser user;
|
||||
|
||||
@Inject
|
||||
ServerRequestContext(InternalUser.Factory userFactory) {
|
||||
this.user = userFactory.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrentUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<ReviewDb> getReviewDbProvider() {
|
||||
return new Provider<ReviewDb>() {
|
||||
@Override
|
||||
public ReviewDb get() {
|
||||
throw new ProvisionException("Automatic ReviewDb only available in request scope");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
126
java/com/google/gerrit/server/util/SocketUtil.java
Normal file
126
java/com/google/gerrit/server/util/SocketUtil.java
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (C) 2009 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.util;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public final class SocketUtil {
|
||||
/** True if this InetAddress is a raw IPv6 in dotted quad notation. */
|
||||
public static boolean isIPv6(InetAddress ip) {
|
||||
return ip instanceof Inet6Address && ip.getHostName().equals(ip.getHostAddress());
|
||||
}
|
||||
|
||||
/** Get the name or IP address, or {@code *} if this address is a wildcard IP. */
|
||||
public static String hostname(InetSocketAddress addr) {
|
||||
if (addr.getAddress() != null) {
|
||||
if (addr.getAddress().isAnyLocalAddress()) {
|
||||
return "*";
|
||||
}
|
||||
return addr.getAddress().getHostName();
|
||||
}
|
||||
return addr.getHostName();
|
||||
}
|
||||
|
||||
/** Format an address string into {@code host:port} or {@code *:port} syntax. */
|
||||
public static String format(SocketAddress s, int defaultPort) {
|
||||
if (s instanceof InetSocketAddress) {
|
||||
final InetSocketAddress addr = (InetSocketAddress) s;
|
||||
if (addr.getPort() == defaultPort) {
|
||||
return safeHostname(hostname(addr));
|
||||
}
|
||||
return format(hostname(addr), addr.getPort());
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/** Format an address string into {@code host:port} or {@code *:port} syntax. */
|
||||
public static String format(String hostname, int port) {
|
||||
return safeHostname(hostname) + ":" + port;
|
||||
}
|
||||
|
||||
private static String safeHostname(String hostname) {
|
||||
if (0 <= hostname.indexOf(':')) {
|
||||
hostname = "[" + hostname + "]";
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
/** Parse an address string such as {@code host:port} or {@code *:port}. */
|
||||
public static InetSocketAddress parse(String desc, int defaultPort) {
|
||||
String hostStr;
|
||||
String portStr;
|
||||
|
||||
if (desc.startsWith("[")) {
|
||||
// IPv6, as a raw IP address.
|
||||
//
|
||||
final int hostEnd = desc.indexOf(']');
|
||||
if (hostEnd < 0) {
|
||||
throw new IllegalArgumentException("invalid IPv6: " + desc);
|
||||
}
|
||||
|
||||
hostStr = desc.substring(1, hostEnd);
|
||||
portStr = desc.substring(hostEnd + 1);
|
||||
} else {
|
||||
// IPv4, or a host name.
|
||||
//
|
||||
final int hostEnd = desc.indexOf(':');
|
||||
hostStr = 0 <= hostEnd ? desc.substring(0, hostEnd) : desc;
|
||||
portStr = 0 <= hostEnd ? desc.substring(hostEnd) : "";
|
||||
}
|
||||
|
||||
if ("".equals(hostStr)) {
|
||||
hostStr = "*";
|
||||
}
|
||||
if (portStr.startsWith(":")) {
|
||||
portStr = portStr.substring(1);
|
||||
}
|
||||
|
||||
final int port;
|
||||
if (portStr.length() > 0) {
|
||||
try {
|
||||
port = Integer.parseInt(portStr);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("invalid port: " + desc);
|
||||
}
|
||||
} else {
|
||||
port = defaultPort;
|
||||
}
|
||||
|
||||
if ("*".equals(hostStr)) {
|
||||
return new InetSocketAddress(port);
|
||||
}
|
||||
return InetSocketAddress.createUnresolved(hostStr, port);
|
||||
}
|
||||
|
||||
/** Parse and resolve an address string, looking up the IP address. */
|
||||
public static InetSocketAddress resolve(String desc, int defaultPort) {
|
||||
final InetSocketAddress addr = parse(desc, defaultPort);
|
||||
if (addr.getAddress() != null && addr.getAddress().isAnyLocalAddress()) {
|
||||
return addr;
|
||||
}
|
||||
try {
|
||||
final InetAddress host = InetAddress.getByName(addr.getHostName());
|
||||
return new InetSocketAddress(host, addr.getPort());
|
||||
} catch (UnknownHostException e) {
|
||||
throw new IllegalArgumentException("unknown host: " + desc, e);
|
||||
}
|
||||
}
|
||||
|
||||
private SocketUtil() {}
|
||||
}
|
||||
145
java/com/google/gerrit/server/util/SubmoduleSectionParser.java
Normal file
145
java/com/google/gerrit/server/util/SubmoduleSectionParser.java
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright (C) 2011 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.util;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
|
||||
/**
|
||||
* It parses from a configuration file submodule sections.
|
||||
*
|
||||
* <p>Example of submodule sections:
|
||||
*
|
||||
* <pre>
|
||||
* [submodule "project-a"]
|
||||
* url = http://localhost/a
|
||||
* path = a
|
||||
* branch = .
|
||||
*
|
||||
* [submodule "project-b"]
|
||||
* url = http://localhost/b
|
||||
* path = b
|
||||
* branch = refs/heads/test
|
||||
* </pre>
|
||||
*/
|
||||
public class SubmoduleSectionParser {
|
||||
|
||||
private final Config bbc;
|
||||
private final String canonicalWebUrl;
|
||||
private final Branch.NameKey superProjectBranch;
|
||||
|
||||
public SubmoduleSectionParser(
|
||||
Config bbc, String canonicalWebUrl, Branch.NameKey superProjectBranch) {
|
||||
this.bbc = bbc;
|
||||
this.canonicalWebUrl = canonicalWebUrl;
|
||||
this.superProjectBranch = superProjectBranch;
|
||||
}
|
||||
|
||||
public Set<SubmoduleSubscription> parseAllSections() {
|
||||
Set<SubmoduleSubscription> parsedSubscriptions = new HashSet<>();
|
||||
for (String id : bbc.getSubsections("submodule")) {
|
||||
final SubmoduleSubscription subscription = parse(id);
|
||||
if (subscription != null) {
|
||||
parsedSubscriptions.add(subscription);
|
||||
}
|
||||
}
|
||||
return parsedSubscriptions;
|
||||
}
|
||||
|
||||
private SubmoduleSubscription parse(String id) {
|
||||
final String url = bbc.getString("submodule", id, "url");
|
||||
final String path = bbc.getString("submodule", id, "path");
|
||||
String branch = bbc.getString("submodule", id, "branch");
|
||||
|
||||
try {
|
||||
if (url != null
|
||||
&& url.length() > 0
|
||||
&& path != null
|
||||
&& path.length() > 0
|
||||
&& branch != null
|
||||
&& branch.length() > 0) {
|
||||
// All required fields filled.
|
||||
String project;
|
||||
|
||||
if (branch.equals(".")) {
|
||||
branch = superProjectBranch.get();
|
||||
}
|
||||
|
||||
// relative URL
|
||||
if (url.startsWith("../")) {
|
||||
// prefix with a slash for easier relative path walks
|
||||
project = '/' + superProjectBranch.getParentKey().get();
|
||||
String hostPart = url;
|
||||
while (hostPart.startsWith("../")) {
|
||||
int lastSlash = project.lastIndexOf('/');
|
||||
if (lastSlash < 0) {
|
||||
// too many levels up, ignore for now
|
||||
return null;
|
||||
}
|
||||
project = project.substring(0, lastSlash);
|
||||
hostPart = hostPart.substring(3);
|
||||
}
|
||||
project = project + "/" + hostPart;
|
||||
|
||||
// remove leading '/'
|
||||
project = project.substring(1);
|
||||
} else {
|
||||
// It is actually an URI. It could be ssh://localhost/project-a.
|
||||
URI targetServerURI = new URI(url);
|
||||
URI thisServerURI = new URI(canonicalWebUrl);
|
||||
String thisHost = thisServerURI.getHost();
|
||||
String targetHost = targetServerURI.getHost();
|
||||
if (thisHost == null || targetHost == null || !targetHost.equalsIgnoreCase(thisHost)) {
|
||||
return null;
|
||||
}
|
||||
String p1 = targetServerURI.getPath();
|
||||
String p2 = thisServerURI.getPath();
|
||||
if (!p1.startsWith(p2)) {
|
||||
// When we are running the server at
|
||||
// http://server/my-gerrit/ but the subscription is for
|
||||
// http://server/other-teams-gerrit/
|
||||
return null;
|
||||
}
|
||||
// skip common part
|
||||
project = p1.substring(p2.length());
|
||||
}
|
||||
|
||||
while (project.startsWith("/")) {
|
||||
project = project.substring(1);
|
||||
}
|
||||
|
||||
if (project.endsWith(Constants.DOT_GIT_EXT)) {
|
||||
project =
|
||||
project.substring(
|
||||
0, //
|
||||
project.length() - Constants.DOT_GIT_EXT.length());
|
||||
}
|
||||
Project.NameKey projectKey = new Project.NameKey(project);
|
||||
return new SubmoduleSubscription(
|
||||
superProjectBranch, new Branch.NameKey(projectKey, branch), path);
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
// Error in url syntax (in fact it is uri syntax)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
130
java/com/google/gerrit/server/util/SystemLog.java
Normal file
130
java/com/google/gerrit/server/util/SystemLog.java
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2014 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.util;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.Die;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import org.apache.log4j.Appender;
|
||||
import org.apache.log4j.AsyncAppender;
|
||||
import org.apache.log4j.DailyRollingFileAppender;
|
||||
import org.apache.log4j.Layout;
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.helpers.OnlyOnceErrorHandler;
|
||||
import org.apache.log4j.spi.ErrorHandler;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class SystemLog {
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(SystemLog.class);
|
||||
|
||||
public static final String LOG4J_CONFIGURATION = "log4j.configuration";
|
||||
|
||||
private final SitePaths site;
|
||||
private final Config config;
|
||||
|
||||
@Inject
|
||||
public SystemLog(SitePaths site, @GerritServerConfig Config config) {
|
||||
this.site = site;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static boolean shouldConfigure() {
|
||||
return Strings.isNullOrEmpty(System.getProperty(LOG4J_CONFIGURATION));
|
||||
}
|
||||
|
||||
public static Appender createAppender(Path logdir, String name, Layout layout) {
|
||||
final DailyRollingFileAppender dst = new DailyRollingFileAppender();
|
||||
dst.setName(name);
|
||||
dst.setLayout(layout);
|
||||
dst.setEncoding(UTF_8.name());
|
||||
dst.setFile(resolve(logdir).resolve(name).toString());
|
||||
dst.setImmediateFlush(true);
|
||||
dst.setAppend(true);
|
||||
dst.setErrorHandler(new DieErrorHandler());
|
||||
dst.activateOptions();
|
||||
dst.setErrorHandler(new OnlyOnceErrorHandler());
|
||||
return dst;
|
||||
}
|
||||
|
||||
public AsyncAppender createAsyncAppender(String name, Layout layout) {
|
||||
AsyncAppender async = new AsyncAppender();
|
||||
async.setName(name);
|
||||
async.setBlocking(true);
|
||||
async.setBufferSize(config.getInt("core", "asyncLoggingBufferSize", 64));
|
||||
async.setLocationInfo(false);
|
||||
|
||||
if (shouldConfigure()) {
|
||||
async.addAppender(createAppender(site.logs_dir, name, layout));
|
||||
} else {
|
||||
Appender appender = LogManager.getLogger(name).getAppender(name);
|
||||
if (appender != null) {
|
||||
async.addAppender(appender);
|
||||
} else {
|
||||
log.warn(
|
||||
"No appender with the name: " + name + " was found. " + name + " logging is disabled");
|
||||
}
|
||||
}
|
||||
async.activateOptions();
|
||||
return async;
|
||||
}
|
||||
|
||||
private static Path resolve(Path p) {
|
||||
try {
|
||||
return p.toRealPath().normalize();
|
||||
} catch (IOException e) {
|
||||
return p.toAbsolutePath().normalize();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DieErrorHandler implements ErrorHandler {
|
||||
@Override
|
||||
public void error(String message, Exception e, int errorCode, LoggingEvent event) {
|
||||
error(e != null ? e.getMessage() : message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message, Exception e, int errorCode) {
|
||||
error(e != null ? e.getMessage() : message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message) {
|
||||
throw new Die("Cannot open log file: " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateOptions() {}
|
||||
|
||||
@Override
|
||||
public void setAppender(Appender appender) {}
|
||||
|
||||
@Override
|
||||
public void setBackupAppender(Appender appender) {}
|
||||
|
||||
@Override
|
||||
public void setLogger(Logger logger) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2012 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.util;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.errors.NotSignedInException;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
* ThreadLocalRequestContext manages the current RequestContext using a ThreadLocal. When the
|
||||
* context is set, the fields exposed by the context are considered in scope. Otherwise, the
|
||||
* FallbackRequestContext is used.
|
||||
*/
|
||||
public class ThreadLocalRequestContext {
|
||||
private static final String FALLBACK = "FALLBACK";
|
||||
|
||||
public static Module module() {
|
||||
return new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ThreadLocalRequestContext.class);
|
||||
bind(RequestContext.class)
|
||||
.annotatedWith(Names.named(FALLBACK))
|
||||
.to(FallbackRequestContext.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
RequestContext provideRequestContext(@Named(FALLBACK) RequestContext fallback) {
|
||||
return MoreObjects.firstNonNull(local.get(), fallback);
|
||||
}
|
||||
|
||||
@Provides
|
||||
CurrentUser provideCurrentUser(RequestContext ctx) {
|
||||
return ctx.getUser();
|
||||
}
|
||||
|
||||
@Provides
|
||||
IdentifiedUser provideCurrentUser(CurrentUser user) {
|
||||
if (user.isIdentifiedUser()) {
|
||||
return user.asIdentifiedUser();
|
||||
}
|
||||
throw new ProvisionException(NotSignedInException.MESSAGE, new NotSignedInException());
|
||||
}
|
||||
|
||||
@Provides
|
||||
ReviewDb provideReviewDb(RequestContext ctx) {
|
||||
return ctx.getReviewDbProvider().get();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static final ThreadLocal<RequestContext> local = new ThreadLocal<>();
|
||||
|
||||
@Inject
|
||||
ThreadLocalRequestContext() {}
|
||||
|
||||
public RequestContext setContext(@Nullable RequestContext ctx) {
|
||||
RequestContext old = getContext();
|
||||
local.set(ctx);
|
||||
return old;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RequestContext getContext() {
|
||||
return local.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (C) 2012 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.util;
|
||||
|
||||
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
|
||||
import com.google.inject.OutOfScopeException;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Scope;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* {@link RequestScopePropagator} implementation for request scopes based on a {@link ThreadLocal}
|
||||
* context.
|
||||
*
|
||||
* @param <C> "context" type stored in the {@link ThreadLocal}.
|
||||
*/
|
||||
public abstract class ThreadLocalRequestScopePropagator<C> extends RequestScopePropagator {
|
||||
|
||||
private final ThreadLocal<C> threadLocal;
|
||||
|
||||
protected ThreadLocalRequestScopePropagator(
|
||||
Scope scope,
|
||||
ThreadLocal<C> threadLocal,
|
||||
ThreadLocalRequestContext local,
|
||||
Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
|
||||
super(scope, local, dbProviderProvider);
|
||||
this.threadLocal = threadLocal;
|
||||
}
|
||||
|
||||
/** @see RequestScopePropagator#wrap(Callable) */
|
||||
@Override
|
||||
protected final <T> Callable<T> wrapImpl(Callable<T> callable) {
|
||||
C ctx = continuingContext(requireContext());
|
||||
return () -> {
|
||||
C old = threadLocal.get();
|
||||
threadLocal.set(ctx);
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
if (old != null) {
|
||||
threadLocal.set(old);
|
||||
} else {
|
||||
threadLocal.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private C requireContext() {
|
||||
C context = threadLocal.get();
|
||||
if (context == null) {
|
||||
throw new OutOfScopeException("Cannot access scoped object");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new context object based on the passed in context that has no request scoped objects
|
||||
* initialized.
|
||||
*
|
||||
* <p>Note that some code paths expect request-scoped objects like {@code CurrentUser} to be
|
||||
* constructible starting from just the context object returned by this method. For example, in
|
||||
* the SSH scope, the context includes the {@code SshSession}, which is used by {@code
|
||||
* SshCurrentUserProvider} to construct a new {@code CurrentUser} in the new thread.
|
||||
*
|
||||
* @param ctx the context to continue.
|
||||
* @return a new context.
|
||||
*/
|
||||
protected abstract C continuingContext(C ctx);
|
||||
}
|
||||
94
java/com/google/gerrit/server/util/TreeFormatter.java
Normal file
94
java/com/google/gerrit/server/util/TreeFormatter.java
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2011 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.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.SortedSet;
|
||||
|
||||
public class TreeFormatter {
|
||||
|
||||
public interface TreeNode {
|
||||
String getDisplayName();
|
||||
|
||||
boolean isVisible();
|
||||
|
||||
SortedSet<? extends TreeNode> getChildren();
|
||||
}
|
||||
|
||||
public static final String NOT_VISIBLE_NODE = "(x)";
|
||||
|
||||
private static final String NODE_PREFIX = "|-- ";
|
||||
private static final String LAST_NODE_PREFIX = "`-- ";
|
||||
private static final String DEFAULT_TAB_SEPARATOR = "|";
|
||||
|
||||
private final PrintWriter stdout;
|
||||
private String currentTabSeparator = " ";
|
||||
|
||||
public TreeFormatter(PrintWriter stdout) {
|
||||
this.stdout = stdout;
|
||||
}
|
||||
|
||||
public void printTree(SortedSet<? extends TreeNode> rootNodes) {
|
||||
if (rootNodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (rootNodes.size() == 1) {
|
||||
printTree(rootNodes.first());
|
||||
} else {
|
||||
currentTabSeparator = DEFAULT_TAB_SEPARATOR;
|
||||
int i = 0;
|
||||
final int size = rootNodes.size();
|
||||
for (TreeNode rootNode : rootNodes) {
|
||||
final boolean isLastRoot = ++i == size;
|
||||
if (isLastRoot) {
|
||||
currentTabSeparator = " ";
|
||||
}
|
||||
printTree(rootNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void printTree(TreeNode rootNode) {
|
||||
printTree(rootNode, 0, true);
|
||||
}
|
||||
|
||||
private void printTree(TreeNode node, int level, boolean isLast) {
|
||||
printNode(node, level, isLast);
|
||||
final SortedSet<? extends TreeNode> childNodes = node.getChildren();
|
||||
int i = 0;
|
||||
final int size = childNodes.size();
|
||||
for (TreeNode childNode : childNodes) {
|
||||
final boolean isLastChild = ++i == size;
|
||||
printTree(childNode, level + 1, isLastChild);
|
||||
}
|
||||
}
|
||||
|
||||
private void printIndention(int level) {
|
||||
if (level > 0) {
|
||||
stdout.print(String.format("%-" + 4 * level + "s", currentTabSeparator));
|
||||
}
|
||||
}
|
||||
|
||||
private void printNode(TreeNode node, int level, boolean isLast) {
|
||||
printIndention(level);
|
||||
stdout.print(isLast ? LAST_NODE_PREFIX : NODE_PREFIX);
|
||||
if (node.isVisible()) {
|
||||
stdout.print(node.getDisplayName());
|
||||
} else {
|
||||
stdout.print(NOT_VISIBLE_NODE);
|
||||
}
|
||||
stdout.print("\n");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user