Allow SSH commands to be registered dynamically

A new top level command can be added to the DispatchCommandProvider
that has the annotation @CommandName(Commands.ROOT). Once registered
it can be removed later by calling remove() on the supplied handle.

Change-Id: I8e60aa0f19e16ca3f21926b03f0be46537b5ef7a
This commit is contained in:
Shawn O. Pearce
2012-05-08 12:51:50 -07:00
committed by gerrit code review
parent cb0ca18ab1
commit b4992582d6
2 changed files with 43 additions and 13 deletions

View File

@@ -0,0 +1,21 @@
// 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.guice;
/** Handle for registered information. */
public interface RegistrationHandle {
/** Delete this registration. */
public void remove();
}

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.sshd;
import com.google.common.collect.Maps;
import com.google.gerrit.server.guice.RegistrationHandle;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
@@ -23,11 +25,8 @@ import com.google.inject.TypeLiteral;
import org.apache.sshd.server.Command;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentMap;
/**
* Creates DispatchCommand using commands registered by {@link CommandModule}.
@@ -42,7 +41,7 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
private final String dispatcherName;
private final CommandName parent;
private volatile Map<String, Provider<Command>> map;
private volatile ConcurrentMap<String, Provider<Command>> map;
public DispatchCommandProvider(final CommandName cn) {
this(Commands.nameOf(cn), cn);
@@ -59,7 +58,21 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
return factory.create(dispatcherName, getMap());
}
private Map<String, Provider<Command>> getMap() {
public RegistrationHandle register(final CommandName name,
final Provider<Command> cmd) {
final ConcurrentMap<String, Provider<Command>> m = getMap();
if (m.putIfAbsent(name.value(), cmd) != null) {
throw new IllegalArgumentException(name.value() + " exists");
}
return new RegistrationHandle() {
@Override
public void remove() {
m.remove(name.value(), cmd);
}
};
}
private ConcurrentMap<String, Provider<Command>> getMap() {
if (map == null) {
synchronized (this) {
if (map == null) {
@@ -71,10 +84,8 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
}
@SuppressWarnings("unchecked")
private Map<String, Provider<Command>> createMap() {
final Map<String, Provider<Command>> m =
new TreeMap<String, Provider<Command>>();
private ConcurrentMap<String, Provider<Command>> createMap() {
ConcurrentMap<String, Provider<Command>> m = Maps.newConcurrentMap();
for (final Binding<?> b : allCommands()) {
final Annotation annotation = b.getKey().getAnnotation();
if (annotation instanceof CommandName) {
@@ -84,9 +95,7 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
}
}
}
return Collections.unmodifiableMap(
new LinkedHashMap<String, Provider<Command>>(m));
return m;
}
private static final TypeLiteral<Command> type =