Merge branch 'stable-2.8' into stable-2.9

* stable-2.8:
  Emit ref-updated event when editing project access via web UI
  Fix ChangeListener auto-registered implementations
  Fix: The email notification of review comments gets stuck.
  Use consistent grammatical tense in command descriptions
  Make skip bar more user friendly
  Bump version to 2.8.4 in plugin API and archetypes
  Helper script to update API version in plugin archetype pom files
  Serialize GWT dbg and opt compiles
  Bump GERRIT_VERSION to 2.8.4
  Update the mysql documentation concerning charsets
  By default don't allow admins to create new branches by push
  Disable commitWithin when running Reindex
  Emit ref-updated event when editing project access via web UI
  SideBySide2: Fix syntax highlighting for shell files
  ChangeScreen2: Respect comment visibility strategy
  Don't add "Patch File" download link for merge commits

The change 'ChangeScreen2: Respect comment visibility strategy' [1]
is dropped intentionally, see [2].

[1] https://gerrit-review.googlesource.com/55081
[2] https://gerrit-review.googlesource.com/#/c/55536/3/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java

Conflicts:
	Documentation/config-gerrit.txt
	VERSION
	gerrit-gwtui/BUCK
	gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
	gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
	gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
	gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
	gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
	gerrit-plugin-archetype/pom.xml
	gerrit-plugin-gwt-archetype/pom.xml
	gerrit-plugin-gwtui/pom.xml
	gerrit-plugin-js-archetype/pom.xml
	gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
	gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
	gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListMembersCommand.java

Change-Id: I16c848cb385080fd25555c983a3030e0f1cc560b
This commit is contained in:
David Pursehouse 2014-04-04 10:27:13 +09:00 committed by Edwin Kempin
commit 511a35bb6c
27 changed files with 282 additions and 69 deletions

View File

@ -1955,18 +1955,16 @@ Determines the period at which changes are automatically committed to
stable store on disk. This is a costly operation and may block stable store on disk. This is a costly operation and may block
additional index writes, so lower with caution. additional index writes, so lower with caution.
+ +
If zero or negative, changes are committed after every write. This is If zero, changes are committed after every write. This is very costly
very costly but may be useful if offline reindexing is infeasible, or but may be useful if offline reindexing is infeasible, or for development
for development servers. servers.
+ +
Values can be specified using standard time unit abbreviations (`ms`, Values can be specified using standard time unit abbreviations (`ms`, `sec`,
`sec`, `min`, etc.). `min`, etc.).
+ +
This setting also applies when running the reindex program. If it is If negative, `commitWithin` is disabled. Changes are flushed to disk when
configured to commit on every write, this will cause reindex to take the in-memory buffer fills, but only committed and guaranteed to be synced
an unnecessarily long time to complete on sites that have a lot of to disk when the process finishes.
changes. It is recommended to temporarily set a higher value while
running reindex.
Defaults to 300000 ms (5 minutes). Defaults to 300000 ms (5 minutes).
@ -2492,6 +2490,16 @@ only the default internal rules will be used.
+ +
Default is true, to execute project specific rules. Default is true, to execute project specific rules.
[[execution]]
=== Section execution
[[execution.defaultThreadPoolSize]]execution.defaultThreadPoolSize::
+
The default size of the background execution thread pool in
which miscellaneous tasks are handled.
+
Default is 1.
[[sendemail]] [[sendemail]]
=== Section sendemail === Section sendemail
@ -2502,6 +2510,26 @@ and all other properties of section sendemail are ignored.
+ +
By default, true, allowing notifications to be sent. By default, true, allowing notifications to be sent.
[[sendemail.connectTimeout]]sendemail.connectTimeout::
+
The connection timeout of opening a socket connected to a
remote SMTP server.
+
Values can be specified using standard time unit abbreviations
('ms', 'sec', 'min', etc.).
If no unit is specified, milliseconds is assumed.
+
Default is 0. A timeout of zero is interpreted as an infinite
timeout. The connection will then block until established or
an error occurs.
[[sendemail.threadPoolSize]]sendemail.threadPoolSize::
+
Maximum size of thread pool in which the review comments
notifications are sent out asynchronously.
+
By default, 1.
[[sendemail.from]]sendemail.from:: [[sendemail.from]]sendemail.from::
+ +
Designates what name and address Gerrit will place in the From Designates what name and address Gerrit will place in the From

View File

@ -52,11 +52,18 @@ rights on it:
CREATE USER 'gerrit2'@'localhost' IDENTIFIED BY 'secret'; CREATE USER 'gerrit2'@'localhost' IDENTIFIED BY 'secret';
CREATE DATABASE reviewdb; CREATE DATABASE reviewdb;
ALTER DATABASE reviewdb charset=latin1;
GRANT ALL ON reviewdb.* TO 'gerrit2'@'localhost'; GRANT ALL ON reviewdb.* TO 'gerrit2'@'localhost';
FLUSH PRIVILEGES; FLUSH PRIVILEGES;
---- ----
If you are using the MyISAM engine, you need to set latin1 as charset:
----
mysql
ALTER DATABASE reviewdb charset=latin1;
----
Visit MySQL's link:http://dev.mysql.com/doc/[documentation] for further Visit MySQL's link:http://dev.mysql.com/doc/[documentation] for further
information regarding using MySQL. information regarding using MySQL.

View File

@ -27,7 +27,7 @@ gwt_application(
name = 'ui_opt', name = 'ui_opt',
module_target = MODULE, module_target = MODULE,
compiler_opts = GWT_COMPILER_OPTS, compiler_opts = GWT_COMPILER_OPTS,
deps = APP_DEPS, deps = APP_DEPS + [':ui_dbg'],
) )
gwt_application( gwt_application(

View File

@ -112,7 +112,9 @@ class DownloadBox extends VerticalPanel {
insertCommand(commandName, copyLabel); insertCommand(commandName, copyLabel);
} }
} }
if (change.revision(revision).commit().parents().length() == 1) {
insertPatch(); insertPatch();
}
insertArchive(); insertArchive();
insertCommand(null, scheme); insertCommand(null, scheme);
} }

View File

@ -116,7 +116,8 @@ class SkipBar extends Composite {
upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND)); upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND));
downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND)); downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND));
} }
skipNum.setText(Integer.toString(skipped)); skipNum.setText(PatchUtil.M.patchSkipRegion(Integer
.toString(skipped)));
} }
static void link(SkipBar barA, SkipBar barB) { static void link(SkipBar barA, SkipBar barB) {

View File

@ -44,9 +44,7 @@ limitations under the License.
<div class='{style.text}'> <div class='{style.text}'>
<ui:msg> <ui:msg>
<g:Anchor ui:field='upArrow' addStyleNames='{style.arrow} {style.anchor}' /> <g:Anchor ui:field='upArrow' addStyleNames='{style.arrow} {style.anchor}' />
<span><ui:msg>... skipped </ui:msg></span>
<g:Anchor ui:field='skipNum' addStyleNames='{style.anchor}' /> <g:Anchor ui:field='skipNum' addStyleNames='{style.anchor}' />
<span><ui:msg> common lines ...</ui:msg></span>
<g:Anchor ui:field='downArrow' addStyleNames=' {style.arrow} {style.anchor}' /> <g:Anchor ui:field='downArrow' addStyleNames=' {style.arrow} {style.anchor}' />
</ui:msg> </ui:msg>
</div> </div>

View File

@ -22,4 +22,5 @@ public interface PatchMessages extends Messages {
String expandBefore(int cnt); String expandBefore(int cnt);
String expandAfter(int cnt); String expandAfter(int cnt);
String draftSaved(Date when); String draftSaved(Date when);
String patchSkipRegion(String lineNumber);
} }

View File

@ -1,3 +1,4 @@
expandBefore = +{0}&#x21e7; expandBefore = +{0}&#x21e7;
expandAfter = +{0}&#x21e9; expandAfter = +{0}&#x21e9;
draftSaved = Draft saved at {0,time,short} draftSaved = Draft saved at {0,time,short}
patchSkipRegion = ... skipped {0} common lines ...

View File

@ -1,2 +1,3 @@
expandBefore = +{0}&#x21e7; expandBefore = +{0}&#x21e7;
expandAfter = +{0}&#x21e9; expandAfter = +{0}&#x21e9;
patchSkipRegion = ... skipped {0} common lines ...

View File

@ -55,6 +55,7 @@ text/x-ruby
shell: shell:
text/x-sh text/x-sh
application/x-shellscript
sql: sql:
text/x-sql text/x-sql
@ -70,5 +71,6 @@ text/xml
application/xml application/xml
application/x-javascript = application/javascript application/x-javascript = application/javascript
application/x-shellscript = text/x-sh
text/x-h = text/x-c++hdr text/x-h = text/x-c++hdr
text/x-java-source = text/x-java text/x-java-source = text/x-java

View File

@ -14,10 +14,14 @@
package com.google.gerrit.httpd.rpc.project; package com.google.gerrit.httpd.rpc.project;
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ProjectAccess; import com.google.gerrit.common.data.ProjectAccess;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project; 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.account.GroupBackend; import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.AllProjectsNameProvider; import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
@ -32,6 +36,7 @@ import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -46,6 +51,8 @@ class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
@Nullable @Assisted String message); @Nullable @Assisted String message);
} }
private final ChangeHooks hooks;
private final IdentifiedUser user;
private final ProjectAccessFactory.Factory projectAccessFactory; private final ProjectAccessFactory.Factory projectAccessFactory;
private final ProjectCache projectCache; private final ProjectCache projectCache;
@ -56,7 +63,7 @@ class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
MetaDataUpdate.User metaDataUpdateFactory, MetaDataUpdate.User metaDataUpdateFactory,
AllProjectsNameProvider allProjects, AllProjectsNameProvider allProjects,
Provider<SetParent> setParent, Provider<SetParent> setParent,
ChangeHooks hooks, IdentifiedUser user,
@Assisted("projectName") Project.NameKey projectName, @Assisted("projectName") Project.NameKey projectName,
@Nullable @Assisted ObjectId base, @Nullable @Assisted ObjectId base,
@Assisted List<AccessSection> sectionList, @Assisted List<AccessSection> sectionList,
@ -67,13 +74,20 @@ class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
parentProjectName, message, true); parentProjectName, message, true);
this.projectAccessFactory = projectAccessFactory; this.projectAccessFactory = projectAccessFactory;
this.projectCache = projectCache; this.projectCache = projectCache;
this.hooks = hooks;
this.user = user;
} }
@Override @Override
protected ProjectAccess updateProjectConfig(ProjectConfig config, protected ProjectAccess updateProjectConfig(ProjectConfig config,
MetaDataUpdate md, boolean parentProjectUpdate) throws IOException, MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
NoSuchProjectException, ConfigInvalidException { NoSuchProjectException, ConfigInvalidException {
config.commit(md); RevCommit commit = config.commit(md);
hooks.doRefUpdatedHook(
new Branch.NameKey(config.getProject().getNameKey(), RefNames.REFS_CONFIG),
base, commit.getId(), user.getAccount());
projectCache.evict(config.getProject()); projectCache.evict(config.getProject());
return projectAccessFactory.create(projectName).call(); return projectAccessFactory.create(projectName).call();
} }

View File

@ -156,7 +156,7 @@ public class LuceneChangeIndex implements ChangeIndex {
static class GerritIndexWriterConfig { static class GerritIndexWriterConfig {
private final IndexWriterConfig luceneConfig; private final IndexWriterConfig luceneConfig;
private final long commitWithinMs; private long commitWithinMs;
private GerritIndexWriterConfig(Version version, Config cfg, String name) { private GerritIndexWriterConfig(Version version, Config cfg, String name) {
luceneConfig = new IndexWriterConfig(version, luceneConfig = new IndexWriterConfig(version,
@ -169,9 +169,13 @@ public class LuceneChangeIndex implements ChangeIndex {
luceneConfig.setMaxBufferedDocs(cfg.getInt( luceneConfig.setMaxBufferedDocs(cfg.getInt(
"index", name, "maxBufferedDocs", "index", name, "maxBufferedDocs",
IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS)); IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS));
commitWithinMs = ConfigUtil.getTimeUnit( try {
cfg, "index", name, "commitWithin", commitWithinMs =
ConfigUtil.getTimeUnit(cfg, "index", name, "commitWithin",
MILLISECONDS.convert(5, MINUTES), MILLISECONDS); MILLISECONDS.convert(5, MINUTES), MILLISECONDS);
} catch (IllegalArgumentException e) {
commitWithinMs = cfg.getLong("index", name, "commitWithin", 0);
}
} }
IndexWriterConfig getLuceneConfig() { IndexWriterConfig getLuceneConfig() {

View File

@ -23,6 +23,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.lucene.LuceneChangeIndex.GerritIndexWriterConfig; import com.google.gerrit.lucene.LuceneChangeIndex.GerritIndexWriterConfig;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.index.TrackingIndexWriter; import org.apache.lucene.index.TrackingIndexWriter;
import org.apache.lucene.search.ControlledRealTimeReopenThread; import org.apache.lucene.search.ControlledRealTimeReopenThread;
@ -41,7 +42,6 @@ import java.io.IOException;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -56,7 +56,6 @@ class SubIndex {
private final SearcherManager searcherManager; private final SearcherManager searcherManager;
private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread; private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread;
private final ConcurrentMap<RefreshListener, Boolean> refreshListeners; private final ConcurrentMap<RefreshListener, Boolean> refreshListeners;
private final ScheduledExecutorService commitExecutor;
SubIndex(File file, GerritIndexWriterConfig writerConfig) throws IOException { SubIndex(File file, GerritIndexWriterConfig writerConfig) throws IOException {
this(FSDirectory.open(file), file.getName(), writerConfig); this(FSDirectory.open(file), file.getName(), writerConfig);
@ -65,44 +64,45 @@ class SubIndex {
SubIndex(Directory dir, final String dirName, SubIndex(Directory dir, final String dirName,
GerritIndexWriterConfig writerConfig) throws IOException { GerritIndexWriterConfig writerConfig) throws IOException {
this.dir = dir; this.dir = dir;
IndexWriter delegateWriter;
final AutoCommitWriter delegateWriter;
long commitPeriod = writerConfig.getCommitWithinMs(); long commitPeriod = writerConfig.getCommitWithinMs();
if (commitPeriod <= 0) {
commitExecutor = null; if (commitPeriod < 0) {
delegateWriter = new IndexWriter(dir, writerConfig.getLuceneConfig());
} else if (commitPeriod == 0) {
delegateWriter = delegateWriter =
new AutoCommitWriter(dir, writerConfig.getLuceneConfig(), true); new AutoCommitWriter(dir, writerConfig.getLuceneConfig(), true);
} else { } else {
commitExecutor = new ScheduledThreadPoolExecutor(1, final AutoCommitWriter autoCommitWriter =
new ThreadFactoryBuilder() new AutoCommitWriter(dir, writerConfig.getLuceneConfig(), false);
delegateWriter = autoCommitWriter;
new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder()
.setNameFormat("Commit-%d " + dirName) .setNameFormat("Commit-%d " + dirName)
.setDaemon(true) .setDaemon(true)
.build()); .build())
delegateWriter = .scheduleAtFixedRate(new Runnable() {
new AutoCommitWriter(dir, writerConfig.getLuceneConfig(), false);
commitExecutor.scheduleAtFixedRate(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
if (delegateWriter.hasUncommittedChanges()) { if (autoCommitWriter.hasUncommittedChanges()) {
delegateWriter.manualFlush(); autoCommitWriter.manualFlush();
delegateWriter.commit(); autoCommitWriter.commit();
} }
} catch (IOException e) { } catch (IOException e) {
log.error("Error committing Lucene index " + dirName, e); log.error("Error committing Lucene index " + dirName, e);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
log.error("Error committing Lucene index " + dirName, e); log.error("Error committing Lucene index " + dirName, e);
try { try {
delegateWriter.close(); autoCommitWriter.close();
} catch (IOException e2) { } catch (IOException e2) {
log.error("SEVERE: Error closing Lucene index " log.error("SEVERE: Error closing Lucene index " + dirName
+ dirName + " after OOM; index may be corrupted.", e); + " after OOM; index may be corrupted.", e);
} }
} }
} }
}, commitPeriod, commitPeriod, MILLISECONDS); }, commitPeriod, commitPeriod, MILLISECONDS);
} }
writer = new TrackingIndexWriter(delegateWriter); writer = new TrackingIndexWriter(delegateWriter);
searcherManager = new SearcherManager( searcherManager = new SearcherManager(
writer.getIndexWriter(), true, new SearcherFactory()); writer.getIndexWriter(), true, new SearcherFactory());

View File

@ -61,6 +61,7 @@ import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.ChangeSchemas; import com.google.gerrit.server.index.ChangeSchemas;
import com.google.gerrit.server.index.IndexCollection; import com.google.gerrit.server.index.IndexCollection;
import com.google.gerrit.server.index.IndexModule; import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.mail.ReplacePatchSetSender; import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.notedb.NoteDbModule; import com.google.gerrit.server.notedb.NoteDbModule;
import com.google.gerrit.server.patch.PatchListCacheImpl; import com.google.gerrit.server.patch.PatchListCacheImpl;
@ -131,6 +132,7 @@ public class Reindex extends SiteProgram {
mustHaveValidSite(); mustHaveValidSite();
dbInjector = createDbInjector(MULTI_USER); dbInjector = createDbInjector(MULTI_USER);
limitThreads(); limitThreads();
disableLuceneAutomaticCommit();
if (version == null) { if (version == null) {
version = ChangeSchemas.getLatest().getVersion(); version = ChangeSchemas.getLatest().getVersion();
} }
@ -229,6 +231,15 @@ public class Reindex extends SiteProgram {
return dbInjector.createChildInjector(modules); return dbInjector.createChildInjector(modules);
} }
private void disableLuceneAutomaticCommit() {
Config cfg =
dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
if (IndexModule.getIndexType(dbInjector) == IndexType.LUCENE) {
cfg.setLong("index", "changes_open", "commitWithin", -1);
cfg.setLong("index", "changes_closed", "commitWithin", -1);
}
}
private class ReviewDbModule extends LifecycleModule { private class ReviewDbModule extends LifecycleModule {
@Override @Override
protected void configure() { protected void configure() {

View File

@ -14,9 +14,10 @@
package com.google.gerrit.common; package com.google.gerrit.common;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.server.events.ChangeEvent; import com.google.gerrit.server.events.ChangeEvent;
@ExtensionPoint
public interface ChangeListener { public interface ChangeListener {
public void onChangeEvent(ChangeEvent event); public void onChangeEvent(ChangeEvent event);
} }

View File

@ -23,7 +23,8 @@ import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.EmailReviewCommentsExecutor;
import com.google.gerrit.server.git.WorkQueue.Executor;
import com.google.gerrit.server.mail.CommentSender; import com.google.gerrit.server.mail.CommentSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory; import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.util.RequestContext; import com.google.gerrit.server.util.RequestContext;
@ -55,7 +56,7 @@ class EmailReviewComments implements Runnable, RequestContext {
List<PatchLineComment> comments); List<PatchLineComment> comments);
} }
private final WorkQueue workQueue; private final Executor sendEmailsExecutor;
private final PatchSetInfoFactory patchSetInfoFactory; private final PatchSetInfoFactory patchSetInfoFactory;
private final CommentSender.Factory commentSenderFactory; private final CommentSender.Factory commentSenderFactory;
private final SchemaFactory<ReviewDb> schemaFactory; private final SchemaFactory<ReviewDb> schemaFactory;
@ -71,7 +72,7 @@ class EmailReviewComments implements Runnable, RequestContext {
@Inject @Inject
EmailReviewComments ( EmailReviewComments (
WorkQueue workQueue, @EmailReviewCommentsExecutor final Executor executor,
PatchSetInfoFactory patchSetInfoFactory, PatchSetInfoFactory patchSetInfoFactory,
CommentSender.Factory commentSenderFactory, CommentSender.Factory commentSenderFactory,
SchemaFactory<ReviewDb> schemaFactory, SchemaFactory<ReviewDb> schemaFactory,
@ -82,7 +83,7 @@ class EmailReviewComments implements Runnable, RequestContext {
@Assisted Account.Id authorId, @Assisted Account.Id authorId,
@Assisted ChangeMessage message, @Assisted ChangeMessage message,
@Assisted List<PatchLineComment> comments) { @Assisted List<PatchLineComment> comments) {
this.workQueue = workQueue; this.sendEmailsExecutor = executor;
this.patchSetInfoFactory = patchSetInfoFactory; this.patchSetInfoFactory = patchSetInfoFactory;
this.commentSenderFactory = commentSenderFactory; this.commentSenderFactory = commentSenderFactory;
this.schemaFactory = schemaFactory; this.schemaFactory = schemaFactory;
@ -96,7 +97,7 @@ class EmailReviewComments implements Runnable, RequestContext {
} }
void sendAsync() { void sendAsync() {
workQueue.getDefaultQueue().submit(this); sendEmailsExecutor.submit(this);
} }
@Override @Override

View File

@ -207,6 +207,10 @@ public class ConfigUtil {
return defaultValue; return defaultValue;
} }
if (s.startsWith("-")/* negative */) {
throw notTimeUnit(section, subsection, setting, valueString);
}
try { try {
return getTimeUnit(s, defaultValue, wantUnit); return getTimeUnit(s, defaultValue, wantUnit);
} catch (IllegalArgumentException notTime) { } catch (IllegalArgumentException notTime) {

View File

@ -0,0 +1,30 @@
// 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.git;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
/**
* Marker on the global {@link WorkQueue.Executor} used by
* {@link EmailReviewComments}.
*/
@Retention(RUNTIME)
@BindingAnnotation
public @interface EmailReviewCommentsExecutor {
}

View File

@ -45,6 +45,15 @@ public class ReceiveCommitsExecutorModule extends AbstractModule {
return queues.createQueue(poolSize, "ReceiveCommits"); return queues.createQueue(poolSize, "ReceiveCommits");
} }
@Provides
@Singleton
@EmailReviewCommentsExecutor
public WorkQueue.Executor createEmailReviewCommentsExecutor(
@GerritServerConfig Config config, WorkQueue queues) {
int poolSize = config.getInt("sendemail", null, "threadPoolSize", 1);
return queues.createQueue(poolSize, "EmailReviewComments");
}
@Provides @Provides
@Singleton @Singleton
@ChangeUpdateExecutor @ChangeUpdateExecutor

View File

@ -18,10 +18,12 @@ import com.google.common.collect.Lists;
import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.reviewdb.client.Project.NameKey; import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.server.util.IdGenerator;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -83,19 +85,21 @@ public class WorkQueue {
}; };
private Executor defaultQueue; private Executor defaultQueue;
private int defaultQueueSize;
private final IdGenerator idGenerator; private final IdGenerator idGenerator;
private final CopyOnWriteArrayList<Executor> queues; private final CopyOnWriteArrayList<Executor> queues;
@Inject @Inject
WorkQueue(final IdGenerator idGenerator) { WorkQueue(final IdGenerator idGenerator, @GerritServerConfig final Config cfg) {
this.idGenerator = idGenerator; this.idGenerator = idGenerator;
this.queues = new CopyOnWriteArrayList<Executor>(); this.queues = new CopyOnWriteArrayList<Executor>();
defaultQueueSize = cfg.getInt("execution", "defaultThreadPoolSize", 1);
} }
/** Get the default work queue, for miscellaneous tasks. */ /** Get the default work queue, for miscellaneous tasks. */
public synchronized Executor getDefaultQueue() { public synchronized Executor getDefaultQueue() {
if (defaultQueue == null) { if (defaultQueue == null) {
defaultQueue = createQueue(1, "WorkQueue"); defaultQueue = createQueue(defaultQueueSize, "WorkQueue");
} }
return defaultQueue; return defaultQueue;
} }

View File

@ -14,6 +14,7 @@
package com.google.gerrit.server.mail; package com.google.gerrit.server.mail;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Version; import com.google.gerrit.common.Version;
import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.ConfigUtil;
@ -39,10 +40,14 @@ import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
/** Sends email via a nearby SMTP server. */ /** Sends email via a nearby SMTP server. */
@Singleton @Singleton
public class SmtpEmailSender implements EmailSender { public class SmtpEmailSender implements EmailSender {
/** The socket's connect timeout (0 = infinite timeout) */
private static final int DEFAULT_CONNECT_TIMEOUT = 0;
public static class Module extends AbstractModule { public static class Module extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
@ -55,6 +60,7 @@ public class SmtpEmailSender implements EmailSender {
} }
private final boolean enabled; private final boolean enabled;
private final int connectTimeout;
private String smtpHost; private String smtpHost;
private int smtpPort; private int smtpPort;
@ -69,6 +75,10 @@ public class SmtpEmailSender implements EmailSender {
@Inject @Inject
SmtpEmailSender(@GerritServerConfig final Config cfg) { SmtpEmailSender(@GerritServerConfig final Config cfg) {
enabled = cfg.getBoolean("sendemail", null, "enable", true); enabled = cfg.getBoolean("sendemail", null, "enable", true);
connectTimeout =
Ints.checkedCast(ConfigUtil.getTimeUnit(cfg, "sendemail", null,
"connectTimeout", DEFAULT_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS));
smtpHost = cfg.getString("sendemail", null, "smtpserver"); smtpHost = cfg.getString("sendemail", null, "smtpserver");
if (smtpHost == null) { if (smtpHost == null) {
@ -240,6 +250,7 @@ public class SmtpEmailSender implements EmailSender {
} }
try { try {
client.setConnectTimeout(connectTimeout);
client.connect(smtpHost, smtpPort); client.connect(smtpHost, smtpPort);
if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) { if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new EmailException("SMTP server rejected connection"); throw new EmailException("SMTP server rejected connection");

View File

@ -16,21 +16,26 @@ package com.google.gerrit.server.project;
import com.google.common.base.CharMatcher; import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritableBoolean; import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType; import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsNameProvider; import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.PluginConfig; import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory; import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.ProjectConfigEntry; import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
@ -41,6 +46,7 @@ import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -78,6 +84,7 @@ public class PutConfig implements RestModifyView<ProjectResource, Input> {
private final AllProjectsNameProvider allProjects; private final AllProjectsNameProvider allProjects;
private final DynamicMap<RestView<ProjectResource>> views; private final DynamicMap<RestView<ProjectResource>> views;
private final Provider<CurrentUser> currentUser; private final Provider<CurrentUser> currentUser;
private final ChangeHooks hooks;
@Inject @Inject
PutConfig(MetaDataUpdate.User metaDataUpdateFactory, PutConfig(MetaDataUpdate.User metaDataUpdateFactory,
@ -89,6 +96,7 @@ public class PutConfig implements RestModifyView<ProjectResource, Input> {
PluginConfigFactory cfgFactory, PluginConfigFactory cfgFactory,
AllProjectsNameProvider allProjects, AllProjectsNameProvider allProjects,
DynamicMap<RestView<ProjectResource>> views, DynamicMap<RestView<ProjectResource>> views,
ChangeHooks hooks,
Provider<CurrentUser> currentUser) { Provider<CurrentUser> currentUser) {
this.metaDataUpdateFactory = metaDataUpdateFactory; this.metaDataUpdateFactory = metaDataUpdateFactory;
this.projectCache = projectCache; this.projectCache = projectCache;
@ -99,6 +107,7 @@ public class PutConfig implements RestModifyView<ProjectResource, Input> {
this.cfgFactory = cfgFactory; this.cfgFactory = cfgFactory;
this.allProjects = allProjects; this.allProjects = allProjects;
this.views = views; this.views = views;
this.hooks = hooks;
this.currentUser = currentUser; this.currentUser = currentUser;
} }
@ -161,7 +170,15 @@ public class PutConfig implements RestModifyView<ProjectResource, Input> {
md.setMessage("Modified project settings\n"); md.setMessage("Modified project settings\n");
try { try {
projectConfig.commit(md); ObjectId baseRev = projectConfig.getRevision();
ObjectId commitRev = projectConfig.commit(md);
// Only fire hook if project was actually changed.
if (!Objects.equal(baseRev, commitRev)) {
IdentifiedUser user = (IdentifiedUser) currentUser.get();
hooks.doRefUpdatedHook(
new Branch.NameKey(projectName, RefNames.REFS_CONFIG),
baseRev, commitRev, user.getAccount());
};
projectCache.evict(projectConfig.getProject()); projectCache.evict(projectConfig.getProject());
gitMgr.setProjectDescription(projectName, p.getDescription()); gitMgr.setProjectDescription(projectName, p.getDescription());
} catch (IOException e) { } catch (IOException e) {

View File

@ -16,13 +16,16 @@ package com.google.gerrit.server.project;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.DefaultInput; import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project; 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.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
@ -32,6 +35,7 @@ import com.google.inject.Inject;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import java.io.IOException; import java.io.IOException;
@ -45,13 +49,16 @@ class PutDescription implements RestModifyView<ProjectResource, Input> {
private final ProjectCache cache; private final ProjectCache cache;
private final MetaDataUpdate.Server updateFactory; private final MetaDataUpdate.Server updateFactory;
private final GitRepositoryManager gitMgr; private final GitRepositoryManager gitMgr;
private final ChangeHooks hooks;
@Inject @Inject
PutDescription(ProjectCache cache, PutDescription(ProjectCache cache,
MetaDataUpdate.Server updateFactory, MetaDataUpdate.Server updateFactory,
ChangeHooks hooks,
GitRepositoryManager gitMgr) { GitRepositoryManager gitMgr) {
this.cache = cache; this.cache = cache;
this.updateFactory = updateFactory; this.updateFactory = updateFactory;
this.hooks = hooks;
this.gitMgr = gitMgr; this.gitMgr = gitMgr;
} }
@ -84,7 +91,14 @@ class PutDescription implements RestModifyView<ProjectResource, Input> {
} }
md.setAuthor(user); md.setAuthor(user);
md.setMessage(msg); md.setMessage(msg);
config.commit(md); ObjectId baseRev = config.getRevision();
ObjectId commitRev = config.commit(md);
// Only fire hook if project was actually changed.
if (!Objects.equal(baseRev, commitRev)) {
hooks.doRefUpdatedHook(
new Branch.NameKey(resource.getNameKey(), RefNames.REFS_CONFIG),
baseRev, commitRev, user.getAccount());
}
cache.evict(ctl.getProject()); cache.evict(ctl.getProject());
gitMgr.setProjectDescription( gitMgr.setProjectDescription(
resource.getNameKey(), resource.getNameKey(),

View File

@ -241,18 +241,21 @@ public class RefControl {
return false; return false;
} }
boolean owner; boolean owner;
boolean admin;
switch (getCurrentUser().getAccessPath()) { switch (getCurrentUser().getAccessPath()) {
case REST_API: case REST_API:
case JSON_RPC: case JSON_RPC:
owner = isOwner(); owner = isOwner();
admin = getCurrentUser().getCapabilities().canAdministrateServer();
break; break;
default: default:
owner = false; owner = false;
admin = false;
} }
if (object instanceof RevCommit) { if (object instanceof RevCommit) {
return getCurrentUser().getCapabilities().canAdministrateServer() return admin
|| (owner && !isBlocked(Permission.CREATE)) || (owner && !isBlocked(Permission.CREATE))
|| (canPerform(Permission.CREATE) && (!existsOnServer && canUpdate() || projectControl || (canPerform(Permission.CREATE) && (!existsOnServer && canUpdate() || projectControl
.canReadCommit(rw, (RevCommit) object))); .canReadCommit(rw, (RevCommit) object)));

View File

@ -41,7 +41,7 @@ import javax.inject.Inject;
/** /**
* Implements a command that allows the user to see the members of a group. * Implements a command that allows the user to see the members of a group.
*/ */
@CommandMetaData(name = "ls-members", description = "Lists the members of a given group", @CommandMetaData(name = "ls-members", description = "List the members of a given group",
runsAt = MASTER_OR_SLAVE) runsAt = MASTER_OR_SLAVE)
public class ListMembersCommand extends BaseCommand { public class ListMembersCommand extends BaseCommand {
@Inject @Inject

View File

@ -43,7 +43,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.List; import java.util.List;
@CommandMetaData(name = "set-members", description = "Modifies members of specific group or number of groups") @CommandMetaData(name = "set-members", description = "Modify members of specific group or number of groups")
public class SetMembersCommand extends SshCommand { public class SetMembersCommand extends SshCommand {
@Option(name = "--add", aliases = {"-a"}, metaVar = "USER", usage = "users that should be added as group member") @Option(name = "--add", aliases = {"-a"}, metaVar = "USER", usage = "users that should be added as group member")

49
tools/version.py Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# 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.
from __future__ import print_function
from optparse import OptionParser
import os.path
import re
import sys
parser = OptionParser()
opts, args = parser.parse_args()
if not len(args):
parser.error('not enough arguments')
elif len(args) > 1:
parser.error('too many arguments')
new_version = args[0]
pattern = re.compile(r'(\s*)<version>[-.\w]+</version>')
for project in ['gerrit-plugin-archetype', 'gerrit-plugin-gwt-archetype',
'gerrit-plugin-gwtui', 'gerrit-plugin-js-archetype']:
pom = os.path.join(project, 'pom.xml')
try:
outxml = ""
found = False
for line in open(pom, "r"):
m = pattern.match(line)
if m and not found:
outxml += "%s<version>%s</version>\n" % (m.group(1), new_version)
found = True
else:
outxml += line
with open(pom, "w") as outfile:
outfile.write(outxml)
except IOError as err:
print('error updating %s: %s' % (pom, err), file=sys.stderr)