/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.remote;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.nifi.remote.codec.FlowFileCodec;
import org.apache.nifi.remote.protocol.ClientProtocol;
import org.apache.nifi.remote.protocol.ServerProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RemoteResourceManager {
private static final Map<String, Class<? extends FlowFileCodec>> codecClassMap;
private static final Map<String, Class<? extends ServerProtocol>> desiredServerProtocolClassMap = new ConcurrentHashMap<>();
private static final Map<String, Class<? extends ClientProtocol>> desiredClientProtocolClassMap = new ConcurrentHashMap<>();
private static final Map<String, Set<Class<? extends ServerProtocol>>> serverProtocolClassMap;
private static final Map<String, Set<Class<? extends ClientProtocol>>> clientProtocolClassMap;
private static final Logger logger = LoggerFactory.getLogger(RemoteResourceManager.class);
static {
final Map<String, Class<? extends FlowFileCodec>> codecMap = new HashMap<>();
final Map<String, Set<Class<? extends ServerProtocol>>> serverProtocolMap = new HashMap<>();
final Map<String, Set<Class<? extends ClientProtocol>>> clientProtocolMap = new HashMap<>();
// load all of the FlowFileCodecs that we know
final ClassLoader classLoader = RemoteResourceManager.class.getClassLoader();
final ServiceLoader<FlowFileCodec> flowFileCodecLoader = ServiceLoader.load(FlowFileCodec.class, classLoader);
final Iterator<FlowFileCodec> codecItr = flowFileCodecLoader.iterator();
while (codecItr.hasNext()) {
final FlowFileCodec codec = codecItr.next();
final Class<? extends FlowFileCodec> clazz = codec.getClass();
final String codecName = codec.getResourceName();
final Class<? extends FlowFileCodec> previousValue = codecMap.put(codecName, clazz);
if (previousValue != null) {
logger.warn("Multiple FlowFileCodec's found with name {}; choosing to use {} in place of {}",
new Object[]{codecName, clazz.getName(), previousValue.getName()});
}
}
final ServiceLoader<ServerProtocol> serverProtocolLoader = ServiceLoader.load(ServerProtocol.class, classLoader);
final Iterator<ServerProtocol> serverItr = serverProtocolLoader.iterator();
while (serverItr.hasNext()) {
final ServerProtocol protocol = serverItr.next();
final Class<? extends ServerProtocol> clazz = protocol.getClass();
final String protocolName = protocol.getResourceName();
Set<Class<? extends ServerProtocol>> classSet = serverProtocolMap.get(protocolName);
if (classSet == null) {
classSet = new HashSet<>();
serverProtocolMap.put(protocolName, classSet);
}
classSet.add(clazz);
}
final ServiceLoader<ClientProtocol> clientProtocolLoader = ServiceLoader.load(ClientProtocol.class, classLoader);
final Iterator<ClientProtocol> clientItr = clientProtocolLoader.iterator();
while (clientItr.hasNext()) {
final ClientProtocol protocol = clientItr.next();
final Class<? extends ClientProtocol> clazz = protocol.getClass();
final String protocolName = protocol.getResourceName();
Set<Class<? extends ClientProtocol>> classSet = clientProtocolMap.get(protocolName);
if (classSet == null) {
classSet = new HashSet<>();
clientProtocolMap.put(protocolName, classSet);
}
classSet.add(clazz);
}
codecClassMap = Collections.unmodifiableMap(codecMap);
clientProtocolClassMap = Collections.unmodifiableMap(clientProtocolMap);
serverProtocolClassMap = Collections.unmodifiableMap(serverProtocolMap);
}
public static boolean isCodecSupported(final String codecName) {
return codecClassMap.containsKey(codecName);
}
public static boolean isCodecSupported(final String codecName, final int version) {
if (!isCodecSupported(codecName)) {
return false;
}
final FlowFileCodec codec = createCodec(codecName);
final VersionNegotiator negotiator = codec.getVersionNegotiator();
return (negotiator.isVersionSupported(version));
}
public static FlowFileCodec createCodec(final String codecName, final int version) {
final FlowFileCodec codec = createCodec(codecName);
final VersionNegotiator negotiator = codec.getVersionNegotiator();
if (!negotiator.isVersionSupported(version)) {
throw new IllegalArgumentException("FlowFile Codec " + codecName + " does not support version " + version);
}
negotiator.setVersion(version);
return codec;
}
private static FlowFileCodec createCodec(final String codecName) {
final Class<? extends FlowFileCodec> codecClass = codecClassMap.get(codecName);
if (codecClass == null) {
throw new IllegalArgumentException("Unknown Codec: " + codecName);
}
try {
return codecClass.newInstance();
} catch (final Exception e) {
throw new RuntimeException("Unable to instantiate class " + codecClass.getName(), e);
}
}
public static Set<String> getSupportedCodecNames() {
return codecClassMap.keySet();
}
public static List<Integer> getSupportedVersions(final String codecName) {
final FlowFileCodec codec = createCodec(codecName);
return codec.getSupportedVersions();
}
public static Set<Class<? extends ClientProtocol>> getClientProtocolClasses(final String protocolName) {
final Set<Class<? extends ClientProtocol>> classes = clientProtocolClassMap.get(protocolName);
if (classes == null) {
return new HashSet<>();
}
return new HashSet<>(classes);
}
public static Set<Class<? extends ServerProtocol>> getServerProtocolClasses(final String protocolName) {
final Set<Class<? extends ServerProtocol>> classes = serverProtocolClassMap.get(protocolName);
if (classes == null) {
return new HashSet<>();
}
return new HashSet<>(classes);
}
public static void setServerProtocolImplementation(final String protocolName, final Class<? extends ServerProtocol> clazz) {
desiredServerProtocolClassMap.put(protocolName, clazz);
}
public static void setClientProtocolImplementation(final String protocolName, final Class<? extends ClientProtocol> clazz) {
desiredClientProtocolClassMap.put(protocolName, clazz);
}
public static ServerProtocol createServerProtocol(final String protocolName) {
final Set<Class<? extends ServerProtocol>> classSet = getServerProtocolClasses(protocolName);
if (classSet.isEmpty()) {
throw new IllegalArgumentException("Unknkown Server Protocol: " + protocolName);
}
Class<? extends ServerProtocol> desiredClass = desiredServerProtocolClassMap.get(protocolName);
if (desiredClass == null && classSet.size() > 1) {
throw new IllegalStateException("Multiple implementations of Server Protocol " + protocolName + " were found and no preferred implementation has been specified");
}
if (desiredClass != null && !classSet.contains(desiredClass)) {
throw new IllegalStateException("Desired implementation of Server Protocol " + protocolName + " is set to " + desiredClass + ", but that Protocol is not registered as a Server Protocol");
}
if (desiredClass == null) {
desiredClass = classSet.iterator().next();
}
try {
return desiredClass.newInstance();
} catch (final Exception e) {
throw new RuntimeException("Unable to instantiate class " + desiredClass.getName(), e);
}
}
public static ClientProtocol createClientProtocol(final String protocolName) {
final Set<Class<? extends ClientProtocol>> classSet = getClientProtocolClasses(protocolName);
if (classSet.isEmpty()) {
throw new IllegalArgumentException("Unknkown Client Protocol: " + protocolName);
}
Class<? extends ClientProtocol> desiredClass = desiredClientProtocolClassMap.get(protocolName);
if (desiredClass == null && classSet.size() > 1) {
throw new IllegalStateException("Multiple implementations of Client Protocol " + protocolName + " were found and no preferred implementation has been specified");
}
if (desiredClass != null && !classSet.contains(desiredClass)) {
throw new IllegalStateException("Desired implementation of Client Protocol " + protocolName + " is set to " + desiredClass + ", but that Protocol is not registered as a Client Protocol");
}
if (desiredClass == null) {
desiredClass = classSet.iterator().next();
}
try {
return desiredClass.newInstance();
} catch (final Exception e) {
throw new RuntimeException("Unable to instantiate class " + desiredClass.getName(), e);
}
}
}