/* * 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.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Constructor; import org.apache.nifi.remote.codec.FlowFileCodec; import org.apache.nifi.remote.exception.HandshakeException; import org.apache.nifi.remote.protocol.ClientProtocol; import org.apache.nifi.remote.protocol.ServerProtocol; public class RemoteResourceFactory extends RemoteResourceInitiator { @SuppressWarnings("unchecked") public static <T extends FlowFileCodec> T receiveCodecNegotiation(final DataInputStream dis, final DataOutputStream dos) throws IOException, HandshakeException { final String codecName = dis.readUTF(); final int version = dis.readInt(); final T codec = (T) RemoteResourceManager.createCodec(codecName, version); final VersionNegotiator negotiator = codec.getVersionNegotiator(); if (negotiator.isVersionSupported(version)) { dos.write(RESOURCE_OK); dos.flush(); negotiator.setVersion(version); return codec; } else { final Integer preferred = negotiator.getPreferredVersion(version); if (preferred == null) { dos.write(ABORT); dos.flush(); throw new HandshakeException("Unable to negotiate an acceptable version of the FlowFileCodec " + codecName); } dos.write(DIFFERENT_RESOURCE_VERSION); dos.writeInt(preferred); dos.flush(); return receiveCodecNegotiation(dis, dos); } } public static void rejectCodecNegotiation(final DataInputStream dis, final DataOutputStream dos, final String explanation) throws IOException { dis.readUTF(); // read codec name dis.readInt(); // read codec version dos.write(ABORT); dos.writeUTF(explanation); dos.flush(); } @SuppressWarnings("unchecked") public static <T extends ClientProtocol> T receiveClientProtocolNegotiation(final DataInputStream dis, final DataOutputStream dos) throws IOException, HandshakeException { final String protocolName = dis.readUTF(); final int version = dis.readInt(); final T protocol = (T) RemoteResourceManager.createClientProtocol(protocolName); final VersionNegotiator negotiator = protocol.getVersionNegotiator(); if (negotiator.isVersionSupported(version)) { dos.write(RESOURCE_OK); dos.flush(); negotiator.setVersion(version); return protocol; } else { final Integer preferred = negotiator.getPreferredVersion(version); if (preferred == null) { dos.write(ABORT); dos.flush(); throw new HandshakeException("Unable to negotiate an acceptable version of the ClientProtocol " + protocolName); } dos.write(DIFFERENT_RESOURCE_VERSION); dos.writeInt(preferred); dos.flush(); return receiveClientProtocolNegotiation(dis, dos); } } @SuppressWarnings("unchecked") public static <T extends ServerProtocol> T receiveServerProtocolNegotiation(final DataInputStream dis, final DataOutputStream dos) throws IOException, HandshakeException { final String protocolName = dis.readUTF(); final int version = dis.readInt(); final T protocol = (T) RemoteResourceManager.createServerProtocol(protocolName); final VersionNegotiator negotiator = protocol.getVersionNegotiator(); if (negotiator.isVersionSupported(version)) { dos.write(RESOURCE_OK); dos.flush(); negotiator.setVersion(version); return protocol; } else { final Integer preferred = negotiator.getPreferredVersion(version); if (preferred == null) { dos.write(ABORT); dos.flush(); throw new HandshakeException("Unable to negotiate an acceptable version of the ServerProtocol " + protocolName); } dos.write(DIFFERENT_RESOURCE_VERSION); dos.writeInt(preferred); dos.flush(); return receiveServerProtocolNegotiation(dis, dos); } } public static <T extends VersionedRemoteResource> T receiveResourceNegotiation(final Class<T> cls, final DataInputStream dis, final DataOutputStream dos, final Class<?>[] constructorArgClasses, final Object[] constructorArgs) throws IOException, HandshakeException { final String resourceClassName = dis.readUTF(); final T resource; try { @SuppressWarnings("unchecked") final Class<T> resourceClass = (Class<T>) Class.forName(resourceClassName); if (!cls.isAssignableFrom(resourceClass)) { throw new HandshakeException("Expected to negotiate a Versioned Resource of type " + cls.getName() + " but received class name of " + resourceClassName); } final Constructor<T> ctr = resourceClass.getConstructor(constructorArgClasses); resource = ctr.newInstance(constructorArgs); } catch (final Throwable t) { dos.write(ABORT); final String errorMsg = "Unable to instantiate Versioned Resource of type " + resourceClassName; dos.writeUTF(errorMsg); dos.flush(); throw new HandshakeException(errorMsg); } final int version = dis.readInt(); final VersionNegotiator negotiator = resource.getVersionNegotiator(); if (negotiator.isVersionSupported(version)) { dos.write(RESOURCE_OK); dos.flush(); negotiator.setVersion(version); return resource; } else { final Integer preferred = negotiator.getPreferredVersion(version); if (preferred == null) { dos.write(ABORT); dos.flush(); throw new HandshakeException("Unable to negotiate an acceptable version of the resource " + resourceClassName); } dos.write(DIFFERENT_RESOURCE_VERSION); dos.writeInt(preferred); dos.flush(); return receiveResourceNegotiation(cls, dis, dos, constructorArgClasses, constructorArgs); } } }