![JAR search and dependency download from the Maven repository](/logo.png)
org.hyperledger.fabric.sdk.NetworkConfig Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fabric-sdk-java Show documentation
Show all versions of fabric-sdk-java Show documentation
Java SDK for Hyperledger fabric project
/*
* Copyright 2017 DTCC, Fujitsu Australia Software Technology, IBM - All Rights Reserved.
*
* 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 org.hyperledger.fabric.sdk;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.JsonValue.ValueType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.hyperledger.fabric.sdk.Channel.PeerOptions;
import org.hyperledger.fabric.sdk.Peer.PeerRole;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.NetworkConfigurationException;
import org.hyperledger.fabric.sdk.helper.Utils;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import static java.lang.String.format;
import static org.hyperledger.fabric.sdk.helper.Utils.isNullOrEmpty;
/**
* Holds details of network and channel configurations typically loaded from an external config file.
*
* Also contains convenience methods for utilizing the config details,
* including the main {@link HFClient#getChannel(String)} method
*/
public class NetworkConfig {
public static final String CLIENT_CERT_BYTES = "clientCertBytes";
public static final String CLIENT_KEY_BYTES = "clientKeyBytes";
public static final String CLIENT_CERT_FILE = "clientCertFile";
public static final String CLIENT_KEY_FILE = "clientKeyFile";
private static final String URL_PROP_NAME = "url";
private final JsonObject jsonConfig;
private final OrgInfo clientOrganization;
private Map orderers;
private Map peers;
/**
* Names of Peers found
*
* @return Collection of peer names found.
*/
public Collection getPeerNames() {
if (peers == null) {
return Collections.emptySet();
} else {
return new HashSet<>(peers.keySet());
}
}
/**
* Names of Orderers found
*
* @return Collection of peer names found.
*/
public Collection getOrdererNames() {
if (orderers == null) {
return Collections.emptySet();
} else {
return new HashSet<>(orderers.keySet());
}
}
private Properties getNodeProperties(String type, String name, Map nodes) {
if (isNullOrEmpty(name)) {
throw new IllegalArgumentException("Parameter name is null or empty.");
}
Node node = nodes.get(name);
if (node == null) {
throw new IllegalArgumentException(format("%s %s not found.", type, name));
}
if (null == node.properties) {
return new Properties();
} else {
return (Properties) node.properties.clone();
}
}
private void setNodeProperties(String type, String name, Map nodes, Properties properties) {
if (isNullOrEmpty(name)) {
throw new IllegalArgumentException("Parameter name is null or empty.");
}
if (properties == null) {
throw new IllegalArgumentException("Parameter properties is null.");
}
Node node = nodes.get(name);
if (node == null) {
throw new IllegalArgumentException(format("%S %s not found.", type, name));
}
Properties ourCopyProps = new Properties();
ourCopyProps.putAll(properties);
node.properties = ourCopyProps;
}
/**
* Get properties for a specific peer.
*
* @param name Name of peer to get the properties for.
* @return The peer's properties.
*/
public Properties getPeerProperties(String name) {
return getNodeProperties("Peer", name, peers);
}
/**
* Get properties for a specific Orderer.
*
* @param name Name of orderer to get the properties for.
* @return The orderer's properties.
*/
public Properties getOrdererProperties(String name) {
return getNodeProperties("Orderer", name, orderers);
}
/**
* Set a specific peer's properties.
*
* @param name The name of the peer's property to set.
* @param properties The properties to set.
*/
public void setPeerProperties(String name, Properties properties) {
setNodeProperties("Peer", name, peers, properties);
}
/**
* Set a specific orderer's properties.
*
* @param name The name of the orderer's property to set.
* @param properties The properties to set.
*/
public void setOrdererProperties(String name, Properties properties) {
setNodeProperties("Orderer", name, orderers, properties);
}
private String getNodeUrl(String type, String name, Map nodes) {
if (isNullOrEmpty(name)) {
throw new IllegalArgumentException("Parameter name is null or empty.");
}
Node node = nodes.get(name);
if (node == null) {
throw new IllegalStateException(format("%s %s not found.", type, name));
}
return node.getUrl();
}
/**
* Get URL for a specific peer.
*
* @param name Name of peer to get the URL for.
* @return The peer's URL.
*/
public String getPeerUrl(String name) {
return getNodeUrl("Peer", name, peers);
}
// Organizations, keyed on org name (and not on mspid!)
private Map organizations;
private static final Log logger = LogFactory.getLog(NetworkConfig.class);
private NetworkConfig(JsonObject jsonConfig) throws NetworkConfigurationException {
this.jsonConfig = jsonConfig;
// Extract the main details
String configName = getJsonValueAsString(jsonConfig.get("name"));
if (configName == null || configName.isEmpty()) {
throw new NetworkConfigurationException("Network config must have a name");
}
String configVersion = getJsonValueAsString(jsonConfig.get("version"));
if (configVersion == null || configVersion.isEmpty()) {
throw new NetworkConfigurationException("Network config must have a version");
// TODO: Validate the version
}
// Preload and create all peers, orderers, etc
createAllPeers();
createAllOrderers();
Map foundCertificateAuthorities = findCertificateAuthorities();
//createAllCertificateAuthorities();
createAllOrganizations(foundCertificateAuthorities);
// Validate the organization for this client
JsonObject jsonClient = getJsonObject(jsonConfig, "client");
String orgName = jsonClient == null ? null : getJsonValueAsString(jsonClient.get("organization"));
if (orgName == null || orgName.isEmpty()) {
throw new NetworkConfigurationException("A client organization must be specified");
}
clientOrganization = getOrganizationInfo(orgName);
if (clientOrganization == null) {
throw new NetworkConfigurationException("Client organization " + orgName + " is not defined");
}
}
/**
* Creates a new NetworkConfig instance configured with details supplied in a YAML file.
*
* @param configFile The file containing the network configuration
* @return A new NetworkConfig instance
* @throws IOException if an error occurs reading the file
* @throws NetworkConfigurationException if the configuration is invalid
*/
public static NetworkConfig fromYamlFile(File configFile) throws IOException, NetworkConfigurationException {
return fromFile(configFile, false);
}
/**
* Creates a new NetworkConfig instance configured with details supplied in a JSON file.
*
* @param configFile The file containing the network configuration
* @return A new NetworkConfig instance
* @throws IOException if an error occurs reading the file
* @throws NetworkConfigurationException if the configuration is invalid
*/
public static NetworkConfig fromJsonFile(File configFile) throws IOException, NetworkConfigurationException {
return fromFile(configFile, true);
}
/**
* Creates a new NetworkConfig instance configured with details supplied in YAML format
*
* @param configStream A stream opened on a YAML document containing network configuration details
* @return A new NetworkConfig instance
* @throws NetworkConfigurationException if the configuration is invalid
*/
public static NetworkConfig fromYamlStream(InputStream configStream) throws NetworkConfigurationException {
logger.trace("NetworkConfig.fromYamlStream...");
// Sanity check
if (configStream == null) {
throw new IllegalArgumentException("configStream must be specified");
}
Yaml yaml = new Yaml(new SafeConstructor());
Map map = yaml.load(configStream);
JsonObjectBuilder builder = Json.createObjectBuilder(map);
JsonObject jsonConfig = builder.build();
return fromJsonObject(jsonConfig);
}
/**
* Creates a new NetworkConfig instance configured with details supplied in JSON format
*
* @param configStream A stream opened on a JSON document containing network configuration details
* @return A new NetworkConfig instance
* @throws NetworkConfigurationException if the configuration is invalid
*/
public static NetworkConfig fromJsonStream(InputStream configStream) throws NetworkConfigurationException {
logger.trace("NetworkConfig.fromJsonStream...");
// Sanity check
if (configStream == null) {
throw new IllegalArgumentException("configStream must be specified");
}
// Read the input stream and convert to JSON
try (JsonReader reader = Json.createReader(configStream)) {
JsonObject jsonConfig = (JsonObject) reader.read();
return fromJsonObject(jsonConfig);
}
}
/**
* Creates a new NetworkConfig instance configured with details supplied in a JSON object
*
* @param jsonConfig JSON object containing network configuration details
* @return A new NetworkConfig instance
* @throws NetworkConfigurationException if the configuration is invalid
*/
public static NetworkConfig fromJsonObject(JsonObject jsonConfig) throws NetworkConfigurationException {
// Sanity check
if (jsonConfig == null) {
throw new IllegalArgumentException("jsonConfig must be specified");
}
if (logger.isTraceEnabled()) {
logger.trace(format("NetworkConfig.fromJsonObject: %s", jsonConfig.toString()));
}
return NetworkConfig.load(jsonConfig);
}
// Loads a NetworkConfig object from a Json or Yaml file
private static NetworkConfig fromFile(File configFile, boolean isJson) throws IOException, NetworkConfigurationException {
// Sanity check
if (configFile == null) {
throw new IllegalArgumentException("configFile must be specified");
}
if (logger.isTraceEnabled()) {
logger.trace(format("NetworkConfig.fromFile: %s isJson = %b", configFile.getAbsolutePath(), isJson));
}
// Json file
try (InputStream stream = new FileInputStream(configFile)) {
return isJson ? fromJsonStream(stream) : fromYamlStream(stream);
}
}
/**
* Returns a new NetworkConfig instance and populates it from the specified JSON object
*
* @param jsonConfig The JSON object containing the config details
* @return A populated NetworkConfig instance
*/
private static NetworkConfig load(JsonObject jsonConfig) throws NetworkConfigurationException {
// Sanity check
if (jsonConfig == null) {
throw new IllegalArgumentException("config must be specified");
}
return new NetworkConfig(jsonConfig);
}
public OrgInfo getClientOrganization() {
return clientOrganization;
}
public OrgInfo getOrganizationInfo(String orgName) {
return organizations.get(orgName);
}
/**
* Find organizations for a peer.
*
* @param peerName name of peer
* @return returns map of orgName to {@link OrgInfo} that the peer belongs to.
*/
public Map getPeerOrgInfos(final String peerName) {
if (Utils.isNullOrEmpty(peerName)) {
throw new IllegalArgumentException("peerName can not be null or empty.");
}
if (organizations == null || organizations.isEmpty()) {
return new HashMap<>();
}
Map ret = new HashMap<>(16);
organizations.forEach((name, orgInfo) -> {
if (orgInfo.getPeerNames().contains(peerName)) {
ret.put(name, orgInfo);
}
});
return ret;
}
/**
* Returns the admin user associated with the client organization
*
* @return The admin user details
* @throws NetworkConfigurationException if the configuration is invalid
*/
public UserInfo getPeerAdmin() throws NetworkConfigurationException {
// Get the details from the client organization
return getPeerAdmin(clientOrganization.getName());
}
/**
* Returns the admin user associated with the specified organization
*
* @param orgName The name of the organization
* @return The admin user details
* @throws NetworkConfigurationException if the configuration is invalid
*/
public UserInfo getPeerAdmin(String orgName) throws NetworkConfigurationException {
OrgInfo org = getOrganizationInfo(orgName);
if (org == null) {
throw new NetworkConfigurationException(format("Organization %s is not defined", orgName));
}
return org.getPeerAdmin();
}
/**
* Returns a channel configured using the details in the Network Configuration file
*
* @param client The associated client
* @param channelName The name of the channel
* @return A configured Channel instance
*/
Channel loadChannel(HFClient client, String channelName) throws NetworkConfigurationException, InvalidArgumentException {
return loadChannel(client, channelName, networkConfigAddPeerHandlerDefault, networkConfigAddOrdererHandlerDefault);
}
/**
* Returns a channel configured using the details in the Network Configuration file
*
* @param client The associated client
* @param channelName The name of the channel
* @return A configured Channel instance
*/
Channel loadChannel(HFClient client,
String channelName,
NetworkConfigAddPeerHandler networkConfigAddPeerHandler,
NetworkConfigAddOrdererHandler networkConfigAddOrdererHandler)
throws NetworkConfigurationException, InvalidArgumentException {
if (logger.isTraceEnabled()) {
logger.trace(format("NetworkConfig.loadChannel: %s", channelName));
}
JsonObject channels = getJsonObject(jsonConfig, "channels");
if (null == channels) {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
}
JsonObject jsonChannel = getJsonObject(channels, channelName);
if (null == jsonChannel) {
final Set channelNames = getChannelNames();
if (channelNames.isEmpty()) {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
}
throw new NetworkConfigurationException(format("Channel %s not found in configuration file. Found channel names: %s ",
channelName, String.join(", ", channelNames)));
}
Channel channel = client.getChannel(channelName);
if (channel != null) {
// The channel already exists in the client!
// Note that by rights this should never happen as HFClient.loadChannelFromConfig should have already checked for this!
throw new NetworkConfigurationException(format("Channel %s is already configured in the client!", channelName));
}
return reconstructChannel(client, channelName, jsonChannel, networkConfigAddPeerHandler, networkConfigAddOrdererHandler);
}
// Creates Node instances representing all the orderers defined in the config file
private void createAllOrderers() throws NetworkConfigurationException {
// Sanity check
if (orderers != null) {
throw new NetworkConfigurationException("INTERNAL ERROR: orderers have already been initialized!");
}
orderers = new HashMap<>();
// orderers is a JSON object containing a nested object for each orderers
JsonObject jsonOrderers = getJsonObject(jsonConfig, "orderers");
if (jsonOrderers != null) {
for (Entry entry : jsonOrderers.entrySet()) {
String ordererName = entry.getKey();
JsonObject jsonOrderer = getJsonValueAsObject(entry.getValue());
if (jsonOrderer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid orderer entry: %s", ordererName));
}
Node orderer = createNode(jsonOrderer);
if (orderer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid orderer entry: %s", ordererName));
}
orderers.put(ordererName, orderer);
}
}
}
// Creates Node instances representing all the peers defined in the config file
private void createAllPeers() throws NetworkConfigurationException {
// Sanity checks
if (peers != null) {
throw new NetworkConfigurationException("INTERNAL ERROR: peers have already been initialized!");
}
peers = new HashMap<>();
// peers is a JSON object containing a nested object for each peer
JsonObject jsonPeers = getJsonObject(jsonConfig, "peers");
//out("Peers: " + (jsonPeers == null ? "null" : jsonPeers.toString()));
if (jsonPeers != null) {
for (Entry entry : jsonPeers.entrySet()) {
String peerName = entry.getKey();
JsonObject jsonPeer = getJsonValueAsObject(entry.getValue());
if (jsonPeer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid peer entry: %s", peerName));
}
Node peer = createNode(jsonPeer);
if (peer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid peer entry: %s", peerName));
}
peers.put(peerName, peer);
}
}
}
// Produce a map from tag to jsonobject for the CA
private Map findCertificateAuthorities() throws NetworkConfigurationException {
Map ret = new HashMap<>();
JsonObject jsonCertificateAuthorities = getJsonObject(jsonConfig, "certificateAuthorities");
if (null != jsonCertificateAuthorities) {
for (Entry entry : jsonCertificateAuthorities.entrySet()) {
String name = entry.getKey();
JsonObject jsonCA = getJsonValueAsObject(entry.getValue());
if (jsonCA == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid CA entry: %s", name));
}
ret.put(name, jsonCA);
}
}
return ret;
}
// Creates JsonObjects representing all the Organizations defined in the config file
private void createAllOrganizations(Map foundCertificateAuthorities) throws NetworkConfigurationException {
// Sanity check
if (organizations != null) {
throw new NetworkConfigurationException("INTERNAL ERROR: organizations have already been initialized!");
}
organizations = new HashMap<>();
// organizations is a JSON object containing a nested object for each Org
JsonObject jsonOrganizations = getJsonObject(jsonConfig, "organizations");
if (jsonOrganizations != null) {
for (Entry entry : jsonOrganizations.entrySet()) {
String orgName = entry.getKey();
JsonObject jsonOrg = getJsonValueAsObject(entry.getValue());
if (jsonOrg == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid Organization entry: %s", orgName));
}
OrgInfo org = createOrg(orgName, jsonOrg, foundCertificateAuthorities);
organizations.put(orgName, org);
}
}
}
// Reconstructs an existing channel
private Channel reconstructChannel(HFClient client,
String channelName,
JsonObject jsonChannel,
NetworkConfigAddPeerHandler networkConfigAddPeerHandler,
NetworkConfigAddOrdererHandler networkConfigAddOrdererHandler)
throws NetworkConfigurationException, InvalidArgumentException {
Channel channel = client.newChannel(channelName);
// orderers is an array of orderer name strings
JsonArray ordererNames = getJsonValueAsArray(jsonChannel.get("orderers"));
//out("Orderer names: " + (ordererNames == null ? "null" : ordererNames.toString()));
if (ordererNames != null) {
for (JsonValue jsonVal : ordererNames) {
String ordererName = getJsonValueAsString(jsonVal);
// Orderer orderer = getOrderer(client, ordererName);
Node node = orderers.get(ordererName);
if (null == node) {
throw new NetworkConfigurationException(format("Error constructing channel %s. Orderer %s not defined in configuration", channelName, ordererName));
}
logger.debug(format("Channel %s, adding orderer %s, url: %s", channel.getName(), ordererName, node.url));
Properties nodeProps = node.properties;
if (null != nodeProps) {
nodeProps = (Properties) nodeProps.clone();
}
networkConfigAddOrdererHandler.addOrderer(this, client, channel, ordererName, node.url, nodeProps, node.jsonObject);
}
}
// peers is an object containing a nested object for each peer
JsonObject jsonPeers = getJsonObject(jsonChannel, "peers");
boolean foundPeer = false;
//out("Peers: " + (peers == null ? "null" : peers.toString()));
if (jsonPeers != null) {
for (Entry entry : jsonPeers.entrySet()) {
String peerName = entry.getKey();
if (logger.isTraceEnabled()) {
logger.trace(format("NetworkConfig.reconstructChannel: Processing peer %s", peerName));
}
JsonObject jsonPeer = getJsonValueAsObject(entry.getValue());
if (jsonPeer == null) {
throw new NetworkConfigurationException(format("Error constructing channel %s. Invalid peer entry: %s", channelName, peerName));
}
Node node = peers.get(peerName);
if (node == null) {
throw new NetworkConfigurationException(format("Error constructing channel %s. Peer %s not defined in configuration", channelName, peerName));
}
// Set the various roles
PeerOptions peerOptions = PeerOptions.createPeerOptions();
for (PeerRole peerRole : PeerRole.values()) {
setPeerRole(channelName, peerOptions, jsonPeer, peerRole);
}
logger.debug(format("Channel %s, adding peer %s, url: %s", channel.getName(), peerName, node.url));
Properties nodeProps = node.properties;
if (null != nodeProps) {
nodeProps = (Properties) nodeProps.clone();
}
networkConfigAddPeerHandler.addPeer(this, client, channel, peerName, node.url, nodeProps, peerOptions, node.jsonObject);
foundPeer = true;
}
}
if (!foundPeer) {
// peers is a required field
throw new NetworkConfigurationException(format("Error constructing channel %s. At least one peer must be specified", channelName));
}
return channel;
}
/**
* Interface defining handler for adding peers.
*/
public interface NetworkConfigAddPeerHandler {
/**
* @param networkConfig The network configuration.
* @param client The client to be used to create the peer.
* @param channel The channel the peer is to be added.
* @param peerName The peer's name.
* @param peerURL The peers's url
* @param peerProperties properties that were found in the networkconfig
* @param peerOptions options when adding peer to the channel.
* @param jsonPeer json peer was created
* @throws NetworkConfigurationException if the configuration cannot be parsed
*/
void addPeer(NetworkConfig networkConfig, HFClient client, Channel channel, String peerName, String peerURL, Properties peerProperties, PeerOptions peerOptions, JsonObject jsonPeer) throws NetworkConfigurationException;
}
private static final NetworkConfigAddPeerHandler networkConfigAddPeerHandlerDefault = (networkConfig, client, channel, peerName, peerURL, peerProperties, peerOptions, jsonPeer) -> {
try {
Peer peer = client.newPeer(peerName, peerURL, peerProperties);
channel.addPeer(peer, peerOptions);
} catch (Exception e) {
throw new NetworkConfigurationException(format("Error on creating channel %s peer %s", channel.getName(), peerName), e);
}
};
/**
* Interface defining handler for adding orderers.
*/
public interface NetworkConfigAddOrdererHandler {
/**
* @param networkConfig The network configuration.
* @param client The client to be used to create the orderer.
* @param channel The channel the orderer is to be added.
* @param ordererName The orderer's name.
* @param ordererURL The orderers's url
* @param ordererProperties properties that were found in the networkconfig
* @param jsonOrderer json orderer was created
* @throws NetworkConfigurationException if the configuration cannot be parsed
*/
void addOrderer(NetworkConfig networkConfig, HFClient client, Channel channel, String ordererName, String ordererURL, Properties ordererProperties, JsonObject jsonOrderer) throws NetworkConfigurationException;
}
private static final NetworkConfigAddOrdererHandler networkConfigAddOrdererHandlerDefault = (networkConfig, client, channel, ordererName, ordererURL, ordererProperties, jsonOrderer) -> {
try {
Orderer orderer = client.newOrderer(ordererName, ordererURL, ordererProperties);
channel.addOrderer(orderer);
} catch (Exception e) {
throw new NetworkConfigurationException(format("Error on creating channel %s orderer %s", channel.getName(), ordererName), e);
}
};
private static void setPeerRole(String channelName, PeerOptions peerOptions, JsonObject jsonPeer, PeerRole role) throws NetworkConfigurationException {
String propName = roleNameRemap(role);
JsonValue val = jsonPeer.get(propName);
if (val != null) {
Boolean isSet = getJsonValueAsBoolean(val);
if (isSet == null) {
// This is an invalid boolean value
throw new NetworkConfigurationException(format("Error constructing channel %s. Role %s has invalid boolean value: %s", channelName, propName, val.toString()));
}
if (isSet) {
peerOptions.addPeerRole(role);
}
}
}
private static final Map roleNameRemapHash = new HashMap<>();
static {
roleNameRemapHash.put(PeerRole.SERVICE_DISCOVERY, "discover");
}
private static String roleNameRemap(PeerRole peerRole) {
String remap = roleNameRemapHash.get(peerRole);
return remap == null ? peerRole.getPropertyName() : remap;
}
// Creates a new Node instance from a JSON object
private Node createNode(JsonObject jsonNode) {
String url = jsonNode.getString(URL_PROP_NAME, null);
if (url == null) {
return null;
}
Properties props = extractProperties(jsonNode, "grpcOptions");
String value = props.getProperty("grpc.keepalive_time_ms");
if (null != value) {
props.remove("grpc.keepalive_time_ms");
props.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {Long.parseLong(value), TimeUnit.MILLISECONDS});
}
value = props.getProperty("grpc.keepalive_timeout_ms");
if (null != value) {
props.remove("grpc.keepalive_timeout_ms");
props.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {Long.parseLong(value), TimeUnit.MILLISECONDS});
}
value = props.getProperty("grpc.keepalive_without_calls");
if (null != value) {
props.remove("grpc.keepalive_without_calls");
props.put("grpc.NettyChannelBuilderOption.keepAliveWithoutCalls", new Object[] {Boolean.valueOf(value)});
}
// Extract the pem details
getTLSCerts(jsonNode, props);
return new Node(url, props, jsonNode);
}
private void getTLSCerts(JsonObject jsonOrderer, Properties props) {
JsonObject jsonTlsCaCerts = getJsonObject(jsonOrderer, "tlsCACerts");
if (jsonTlsCaCerts != null) {
String pemFilename = getJsonValueAsString(jsonTlsCaCerts.get("path"));
if (pemFilename != null) {
// let the sdk handle non existing errors could be they don't exist during parsing but are there later.
props.put("pemFile", pemFilename);
}
byte[] pemBytes = getJsonValueAsList(jsonTlsCaCerts.get("pem"), NetworkConfig::getJsonValueAsString).stream()
.collect(Collectors.joining("\n"))
.getBytes();
props.put("pemBytes", pemBytes);
JsonObject jsonTlsClientCerts = getJsonObject(jsonTlsCaCerts, "client");
if (jsonTlsClientCerts != null) {
String keyfile = getJsonValueAsString(jsonTlsClientCerts.get("keyfile"));
String certfile = getJsonValueAsString(jsonTlsClientCerts.get("certfile"));
if (keyfile != null) {
props.put(CLIENT_KEY_FILE, keyfile);
}
if (certfile != null) {
props.put(CLIENT_CERT_FILE, certfile);
}
String keyBytes = getJsonValueAsString(jsonTlsClientCerts.get("keyPem"));
String certBytes = getJsonValueAsString(jsonTlsClientCerts.get("certPem"));
if (keyBytes != null) {
props.put(CLIENT_KEY_BYTES, keyBytes.getBytes());
}
if (certBytes != null) {
props.put(CLIENT_CERT_BYTES, certBytes.getBytes());
}
}
}
}
// Creates a new OrgInfo instance from a JSON object
private OrgInfo createOrg(String orgName, JsonObject jsonOrg, Map foundCertificateAuthorities) throws NetworkConfigurationException {
String msgPrefix = format("Organization %s", orgName);
String mspId = getJsonValueAsString(jsonOrg.get("mspid"));
OrgInfo org = new OrgInfo(orgName, mspId);
// Peers
JsonArray jsonPeers = getJsonValueAsArray(jsonOrg.get("peers"));
if (jsonPeers != null) {
for (JsonValue peer : jsonPeers) {
final String peerName = getJsonValueAsString(peer);
if (peerName != null) {
org.addPeerName(peerName);
final Node node = peers.get(peerName);
if (null != node) {
if (null == node.properties) {
node.properties = new Properties();
}
node.properties.put(Peer.PEER_ORGANIZATION_MSPID_PROPERTY, org.getMspId());
} else {
throw new NetworkConfigurationException(format("Organization %s has peer %s listed not found in any channel peer list.", orgName, peerName));
}
}
}
}
// CAs
JsonArray jsonCertificateAuthorities = getJsonValueAsArray(jsonOrg.get("certificateAuthorities"));
if (jsonCertificateAuthorities != null) {
for (JsonValue jsonCA : jsonCertificateAuthorities) {
String caName = getJsonValueAsString(jsonCA);
if (caName != null) {
JsonObject jsonObject = foundCertificateAuthorities.get(caName);
if (jsonObject != null) {
org.addCertificateAuthority(createCA(caName, jsonObject, org));
} else {
throw new NetworkConfigurationException(format("%s: Certificate Authority %s is not defined", msgPrefix, caName));
}
}
}
}
String adminPrivateKeyString = extractPemString(jsonOrg, "adminPrivateKey", msgPrefix);
String signedCert = extractPemString(jsonOrg, "signedCert", msgPrefix);
if (!isNullOrEmpty(adminPrivateKeyString) && !isNullOrEmpty(signedCert)) {
final PrivateKey privateKey;
try {
privateKey = getPrivateKeyFromString(adminPrivateKeyString);
} catch (IOException ioe) {
throw new NetworkConfigurationException(format("%s: Invalid private key", msgPrefix), ioe);
}
try {
org.peerAdmin = new UserInfo(mspId, "PeerAdmin_" + mspId + "_" + orgName, null);
} catch (Exception e) {
throw new NetworkConfigurationException(e.getMessage(), e);
}
org.peerAdmin.setEnrollment(new X509Enrollment(privateKey, signedCert));
}
return org;
}
private static PrivateKey getPrivateKeyFromString(String data)
throws IOException {
final Reader pemReader = new StringReader(data);
final PrivateKeyInfo pemPair;
try (PEMParser pemParser = new PEMParser(pemReader)) {
pemPair = (PrivateKeyInfo) pemParser.readObject();
}
return new JcaPEMKeyConverter().getPrivateKey(pemPair);
}
// Returns the PEM (as a String) from either a path or a pem field
private static String extractPemString(JsonObject json, String fieldName, String msgPrefix) throws NetworkConfigurationException {
String path = null;
String pemString = null;
JsonObject jsonField = getJsonValueAsObject(json.get(fieldName));
if (jsonField != null) {
path = getJsonValueAsString(jsonField.get("path"));
pemString = getJsonValueAsString(jsonField.get("pem"));
}
if (path != null && pemString != null) {
throw new NetworkConfigurationException(format("%s should not specify both %s path and pem", msgPrefix, fieldName));
}
if (path != null) {
// Determine full pathname and ensure the file exists
File pemFile = new File(path);
String fullPathname = pemFile.getAbsolutePath();
if (!pemFile.exists()) {
throw new NetworkConfigurationException(format("%s: %s file %s does not exist", msgPrefix, fieldName, fullPathname));
}
try (FileInputStream stream = new FileInputStream(pemFile)) {
pemString = IOUtils.toString(stream, StandardCharsets.UTF_8);
} catch (IOException ioe) {
throw new NetworkConfigurationException(format("Failed to read file: %s", fullPathname), ioe);
}
}
return pemString;
}
// Creates a new CAInfo instance from a JSON object
private CAInfo createCA(String name, JsonObject jsonCA, OrgInfo org) throws NetworkConfigurationException {
String url = getJsonValueAsString(jsonCA.get("url"));
Properties httpOptions = extractProperties(jsonCA, "httpOptions");
List regUsers = new ArrayList<>();
List registrars = getJsonValueAsList(jsonCA.get("registrar"), NetworkConfig::getJsonValueAsObject);
for (JsonObject registrar : registrars) {
String enrollId = getJsonValueAsString(registrar.get("enrollId"));
String enrollSecret = getJsonValueAsString(registrar.get("enrollSecret"));
try {
regUsers.add(new UserInfo(org.mspId, enrollId, enrollSecret));
} catch (Exception e) {
throw new NetworkConfigurationException(e.getMessage(), e);
}
}
CAInfo caInfo = new CAInfo(name, url, regUsers, httpOptions);
String caName = getJsonValueAsString(jsonCA.get("caName"));
if (caName != null) {
caInfo.setCaName(caName);
}
Properties properties = new Properties();
if ("false".equals(httpOptions.getProperty("verify"))) {
properties.setProperty("allowAllHostNames", "true");
}
getTLSCerts(jsonCA, properties);
caInfo.setProperties(properties);
return caInfo;
}
// Extracts all defined properties of the specified field and returns a Properties object
private static Properties extractProperties(JsonObject json, String fieldName) {
Properties props = new Properties();
// Extract any other grpc options
JsonObject options = getJsonObject(json, fieldName);
if (options != null) {
for (Entry entry : options.entrySet()) {
String key = entry.getKey();
JsonValue value = entry.getValue();
props.setProperty(key, getJsonValue(value));
}
}
return props;
}
// Returns the specified JsonValue in a suitable format
// If it's a JsonString - it returns the string
// If it's a number = it returns the string representation of that number
// If it's TRUE or FALSE - it returns "true" and "false" respectively
// If it's anything else it returns null
private static String getJsonValue(JsonValue value) {
String s = null;
if (value != null) {
s = getJsonValueAsString(value);
if (s == null) {
s = getJsonValueAsNumberString(value);
}
if (s == null) {
Boolean b = getJsonValueAsBoolean(value);
if (b != null) {
s = b ? "true" : "false";
}
}
}
return s;
}
// Returns the specified JsonValue as a JsonObject, or null if it's not an object
private static JsonObject getJsonValueAsObject(JsonValue value) {
return (value != null && value.getValueType() == ValueType.OBJECT) ? value.asJsonObject() : null;
}
// Returns the specified JsonValue as a JsonArray, or null if it's not an array
private static JsonArray getJsonValueAsArray(JsonValue value) {
return (value != null && value.getValueType() == ValueType.ARRAY) ? value.asJsonArray() : null;
}
private static List getJsonValueAsList(final JsonValue value, Function map) {
return getJsonValueAsList(value).stream()
.map(map)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
// Returns the specified JsonValue as a List. Allows single or array
private static List getJsonValueAsList(final JsonValue value) {
if (value == null) {
return Collections.emptyList();
}
if (value.getValueType() == ValueType.ARRAY) {
return value.asJsonArray();
}
return Collections.singletonList(value);
}
// Returns the specified JsonValue as a String, or null if it's not a string
private static String getJsonValueAsString(JsonValue value) {
return (value != null && value.getValueType() == ValueType.STRING) ? ((JsonString) value).getString() : null;
}
// Returns the specified JsonValue as a String, or null if it's not a string
private static String getJsonValueAsNumberString(JsonValue value) {
return (value != null && value.getValueType() == ValueType.NUMBER) ? value.toString() : null;
}
// Returns the specified JsonValue as a Boolean, or null if it's not a boolean
private static Boolean getJsonValueAsBoolean(JsonValue value) {
if (value != null) {
if (value.getValueType() == ValueType.TRUE) {
return true;
} else if (value.getValueType() == ValueType.FALSE) {
return false;
}
}
return null;
}
// Returns the specified property as a JsonObject
private static JsonObject getJsonObject(JsonObject object, String propName) {
JsonObject obj = null;
JsonValue val = object.get(propName);
if (val != null && val.getValueType() == ValueType.OBJECT) {
obj = val.asJsonObject();
}
return obj;
}
/**
* Get the channel names found.
*
* @return A set of the channel names found in the configuration file or empty set if none found.
*/
public Set getChannelNames() {
Set result = new HashSet<>();
JsonObject channels = getJsonObject(jsonConfig, "channels");
if (channels != null) {
result.addAll(channels.keySet());
}
return result;
}
// Holds a network "node" (eg. Peer, Orderer)
private static class Node {
private final String url;
public final JsonObject jsonObject;
private Properties properties;
private Node(String url, Properties properties, JsonObject jsonObject) {
this.url = url;
this.properties = properties;
this.jsonObject = jsonObject;
}
private String getUrl() {
return url;
}
}
/**
* Holds details of a User
*/
public static class UserInfo implements User {
public void setName(String name) {
this.name = name;
}
protected String name;
protected String enrollSecret;
protected String mspid;
private Set roles;
private String account;
private String affiliation;
private Enrollment enrollment;
public void setEnrollSecret(String enrollSecret) {
this.enrollSecret = enrollSecret;
}
public String getMspid() {
return mspid;
}
public void setMspid(String mspid) {
this.mspid = mspid;
}
public void setRoles(Set roles) {
this.roles = roles;
}
public void setAccount(String account) {
this.account = account;
}
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public void setEnrollment(Enrollment enrollment) {
this.enrollment = enrollment;
}
UserInfo(String mspid, String name, String enrollSecret) {
this.name = name;
this.enrollSecret = enrollSecret;
this.mspid = mspid;
}
public String getEnrollSecret() {
return enrollSecret;
}
@Override
public String getName() {
return name;
}
@Override
public Set getRoles() {
return roles;
}
@Override
public String getAccount() {
return account;
}
@Override
public String getAffiliation() {
return affiliation;
}
@Override
public Enrollment getEnrollment() {
return enrollment;
}
@Override
public String getMspId() {
return mspid;
}
}
/**
* Holds details of an Organization
*/
public static class OrgInfo {
private final String name;
private final String mspId;
private final List peerNames = new ArrayList<>();
private final List certificateAuthorities = new ArrayList<>();
private UserInfo peerAdmin;
OrgInfo(String orgName, String mspId) {
this.name = orgName;
this.mspId = mspId;
}
private void addPeerName(String peerName) {
peerNames.add(peerName);
}
private void addCertificateAuthority(CAInfo ca) {
certificateAuthorities.add(ca);
}
public String getName() {
return name;
}
public String getMspId() {
return mspId;
}
public List getPeerNames() {
return new LinkedList<>(peerNames);
}
public List getCertificateAuthorities() {
return new LinkedList<>(certificateAuthorities);
}
/**
* Returns the associated admin user
*
* @return The admin user details
*/
public UserInfo getPeerAdmin() {
return peerAdmin;
}
}
/**
* Holds the details of a Certificate Authority
*/
public static class CAInfo {
private final String name;
private final String url;
private final Properties httpOptions;
private String caName; // The "optional" caName specified in the config, as opposed to its "config" name
private Properties properties;
private final List registrars;
CAInfo(String name, String url, List registrars, Properties httpOptions) {
this.name = name;
this.url = url;
this.httpOptions = httpOptions;
this.registrars = registrars;
}
private void setCaName(String caName) {
this.caName = caName;
}
public String getName() {
return name;
}
public String getCAName() {
return caName;
}
public String getUrl() {
return url;
}
public Properties getHttpOptions() {
return httpOptions;
}
void setProperties(Properties properties) {
this.properties = properties;
}
public Properties getProperties() {
return this.properties;
}
public Collection getRegistrars() {
return new LinkedList<>(registrars);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy