Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PIE-2052] Besu CLI -V to print plugin versions #123

Merged
merged 9 commits into from
Oct 22, 2019
5 changes: 4 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.hyperledger.besu.cli.subcommands.blocks.BlocksSubCommand.RlpBlockExporterFactory;
import org.hyperledger.besu.cli.subcommands.operator.OperatorSubCommand;
import org.hyperledger.besu.cli.subcommands.rlp.RLPSubCommand;
import org.hyperledger.besu.cli.util.BesuCommandCustomFactory;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.cli.util.ConfigOptionSearchAndRunHandler;
import org.hyperledger.besu.cli.util.VersionProvider;
Expand Down Expand Up @@ -773,7 +774,9 @@ public void parse(
final BesuExceptionHandler exceptionHandler,
final InputStream in,
final String... args) {
commandLine = new CommandLine(this).setCaseInsensitiveEnumValuesAllowed(true);
commandLine =
new CommandLine(this, new BesuCommandCustomFactory(besuPluginContext))
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
.setCaseInsensitiveEnumValuesAllowed(true);
handleStandaloneCommand()
.addSubCommands(resultHandler, in)
.registerConverters()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.util;

import org.hyperledger.besu.services.PluginVersionsProvider;

import java.lang.reflect.Constructor;

import picocli.CommandLine;

/**
* Custom PicoCli IFactory to handle version provider construction with plugin versions. Based on
* same logic as PicoCLI DefaultFactory.
*/
public class BesuCommandCustomFactory implements CommandLine.IFactory {
private final PluginVersionsProvider pluginVersionsProvider;

public BesuCommandCustomFactory(final PluginVersionsProvider pluginVersionsProvider) {
this.pluginVersionsProvider = pluginVersionsProvider;
}

@SuppressWarnings("unchecked")
@Override
public <T> T create(final Class<T> cls) throws Exception {
if (CommandLine.IVersionProvider.class.isAssignableFrom(cls)) {
return (T) new VersionProvider(pluginVersionsProvider);
}

final Constructor<T> constructor = cls.getDeclaredConstructor();
try {
return constructor.newInstance();
} catch (Exception e) {
constructor.setAccessible(true);
return constructor.newInstance();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,27 @@
package org.hyperledger.besu.cli.util;

import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.services.PluginVersionsProvider;

import java.util.ArrayList;
import java.util.List;

import picocli.CommandLine;

public class VersionProvider implements CommandLine.IVersionProvider {
private final PluginVersionsProvider pluginVersionsProvider;

public VersionProvider(final PluginVersionsProvider pluginVersionsProvider) {
this.pluginVersionsProvider = pluginVersionsProvider;
}

@Override
public String[] getVersion() {
return new String[] {BesuInfo.version()};
// the PluginVersionsProvider has registered plugins and their versions by this time.
final List<String> pluginVersionsList = this.pluginVersionsProvider.getPluginVersions();
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
final List<String> versionsList = new ArrayList<>();
versionsList.add(BesuInfo.version());
versionsList.addAll(pluginVersionsList);
return versionsList.toArray(String[]::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Predicate;
import java.util.stream.Stream;

import com.google.common.annotations.VisibleForTesting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class BesuPluginContextImpl implements BesuContext {
public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvider {

private static final Logger LOG = LogManager.getLogger();

Expand All @@ -57,6 +58,7 @@ private enum Lifecycle {
private Lifecycle state = Lifecycle.UNINITIALIZED;
private final Map<Class<?>, ? super Object> serviceRegistry = new HashMap<>();
private final List<BesuPlugin> plugins = new ArrayList<>();
private final List<String> pluginVersions = new ArrayList<>();
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved

public <T> void addService(final Class<T> serviceType, final T service) {
checkArgument(serviceType.isInterface(), "Services must be Java interfaces.");
Expand Down Expand Up @@ -89,6 +91,7 @@ public void registerPlugins(final Path pluginsDir) {
try {
plugin.register(this);
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
LOG.debug("Registered plugin of type {}.", plugin.getClass().getName());
addPluginVersion(plugin);
} catch (final Exception e) {
LOG.error(
"Error registering plugin of type {}, start and stop will not be called. \n{}",
Expand All @@ -104,6 +107,20 @@ public void registerPlugins(final Path pluginsDir) {
state = Lifecycle.REGISTERED;
}

private void addPluginVersion(final BesuPlugin plugin) {
final Package pluginPackage = plugin.getClass().getPackage();
final String implTitle =
Optional.ofNullable(pluginPackage.getImplementationTitle())
rain-on marked this conversation as resolved.
Show resolved Hide resolved
.filter(Predicate.not(String::isBlank))
.orElse(plugin.getClass().getSimpleName());
final String implVersion =
Optional.ofNullable(pluginPackage.getImplementationVersion())
.filter(Predicate.not(String::isBlank))
.orElse("<Unknown Version>");
final String pluginVersion = implTitle + "/v" + implVersion;
pluginVersions.add(pluginVersion);
}

public void startPlugins() {
checkState(
state == Lifecycle.REGISTERED,
Expand Down Expand Up @@ -153,6 +170,11 @@ public void stopPlugins() {
state = Lifecycle.STOPPED;
}

@Override
public List<String> getPluginVersions() {
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
return Collections.unmodifiableList(pluginVersions);
}

private static URL pathToURIOrNull(final Path p) {
try {
return p.toUri().toURL();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.services;

import java.util.List;

public interface PluginVersionsProvider {
List<String> getPluginVersions();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.util;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.services.PluginVersionsProvider;

import java.util.Arrays;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class BesuCommandCustomFactoryTest {

@Mock private PluginVersionsProvider pluginVersionsProvider;

@Before
public void initMocks() {
when(pluginVersionsProvider.getPluginVersions()).thenReturn(Arrays.asList("v1", "v2"));
}

@Test
public void testCreateVersionProviderInstance() throws Exception {
BesuCommandCustomFactory besuCommandCustomFactory =
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
new BesuCommandCustomFactory(pluginVersionsProvider);
VersionProvider versionProvider = besuCommandCustomFactory.create(VersionProvider.class);
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
assertThat(versionProvider.getVersion()).containsExactly(BesuInfo.version(), "v1", "v2");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.util;

import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.services.PluginVersionsProvider;

import java.util.Collections;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class VersionProviderTest {

@Mock private PluginVersionsProvider pluginVersionsProvider;

@Test
public void validateEmptyListGenerateBesuInfoVersionOnly() {
when(pluginVersionsProvider.getPluginVersions()).thenReturn(emptyList());
VersionProvider versionProvider = new VersionProvider(pluginVersionsProvider);
assertThat(versionProvider.getVersion()).containsOnly(BesuInfo.version());
}

@Test
public void validateVersionListGenerateValidValues() {
when(pluginVersionsProvider.getPluginVersions()).thenReturn(Collections.singletonList("test"));
VersionProvider versionProvider = new VersionProvider(pluginVersionsProvider);
assertThat(versionProvider.getVersion()).containsExactly(BesuInfo.version(), "test");
}
}