All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.table.api.functions.FunctionService.scala Maven / Gradle / Ivy

The newest version!
/*
 * 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.flink.table.api.functions

import org.apache.flink.table.api.ValidationException
import org.apache.flink.table.api.functions.UserDefinedFunction
import org.apache.flink.table.descriptors._
import org.apache.flink.table.util.Logging

import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer

/**
  * Service for creating configured instances of [[UserDefinedFunction]] using a
  * [[FunctionDescriptor]].
  */
object FunctionService extends Logging {

  /**
    * Creates a user-defined function with the given properties and the current thread's
    * context class loader.
    *
    * @param descriptor the descriptor that describes a function
    * @return the generated user-defined function
    */
  def createFunction(descriptor: FunctionDescriptor): UserDefinedFunction = {
    createFunction(descriptor, Thread.currentThread().getContextClassLoader)
  }

  /**
    * Creates a user-defined function with the given properties.
    *
    * @param descriptor the descriptor that describes a function
    * @param classLoader the class loader to load the function and its parameter's classes
    * @param performValidation whether or not the descriptor should be validated
    * @return the generated user-defined function
    */
  def createFunction(
      descriptor: FunctionDescriptor,
      classLoader: ClassLoader,
      performValidation: Boolean = true)
    : UserDefinedFunction = {

    val properties = new DescriptorProperties(true)
    properties.putProperties(descriptor.toProperties)

    // validate
    if (performValidation) {
      new FunctionDescriptorValidator().validate(properties)
    }

    // instantiate
    val (instanceClass, instance) = generateInstance[AnyRef](
      HierarchyDescriptorValidator.EMPTY_PREFIX,
      properties,
      classLoader)

    if (!classOf[UserDefinedFunction].isAssignableFrom(instanceClass)) {
      throw new ValidationException(
        s"Instantiated class '${instanceClass.getName}' is not a user-defined function.")
    }
    instance.asInstanceOf[UserDefinedFunction]
  }

  /**
   * Recursively generate an instance of a class according the given properties.
   *
   * @param keyPrefix the prefix to fetch properties
   * @param descriptorProperties the descriptor properties that contains the class type information
   * @param classLoader the class loader to load the class
   * @tparam T type fo the generated instance
   * @return an instance of the class
   */
  private def generateInstance[T](
      keyPrefix: String,
      descriptorProperties: DescriptorProperties,
      classLoader: ClassLoader)
    : (Class[T], T) = {

    val instanceClassName = descriptorProperties.getString(
      s"$keyPrefix${ClassInstanceValidator.CLASS}")

    val instanceClass = try {
      Class
        .forName(
          descriptorProperties.getString(s"$keyPrefix${ClassInstanceValidator.CLASS}"),
          true,
          classLoader)
        .asInstanceOf[Class[T]]
    } catch {
      case e: Exception =>
        // only log the cause to have clean error messages
        val msg = s"Could not find class '$instanceClassName' for creating an instance."
        LOG.error(msg, e)
        throw new ValidationException(msg)
    }

    val constructorPrefix = s"$keyPrefix${ClassInstanceValidator.CONSTRUCTOR}"

    val constructorProps = descriptorProperties
      .getVariableIndexedProperties(constructorPrefix, List())

    var i = 0
    val parameterList: ArrayBuffer[(Class[_], Any)] = new ArrayBuffer
    while (i < constructorProps.size()) {
      // nested class instance
      if (constructorProps(i).containsKey(ClassInstanceValidator.CLASS)) {
        parameterList += generateInstance(
          s"$constructorPrefix.$i.",
          descriptorProperties,
          classLoader)
      }
      // literal value
      else {
        val literalValue = LiteralValueValidator
          .getValue(s"$constructorPrefix.$i.", descriptorProperties)
        parameterList += ((literalValue.getClass, literalValue))
      }
      i += 1
    }
    val constructor = try {
      instanceClass.getConstructor(parameterList.map(_._1): _*)
    } catch {
      case e: Exception =>
        // only log the cause to have clean error messages
        val msg = s"Cannot find a public constructor with parameter types " +
          s"'${parameterList.map(_._1.getName).mkString(", ")}' for '$instanceClassName'."
        LOG.error(msg, e)
        throw new ValidationException(msg)
    }

    val instance = try {
      constructor.newInstance(parameterList.map(_._2.asInstanceOf[AnyRef]): _*)
    } catch {
      case e: Exception =>
        // only log the cause to have clean error messages
        val msg = s"Error while creating instance of class '$instanceClassName' " +
          s"with parameter types '${parameterList.map(_._1.getName).mkString(", ")}'."
        LOG.error(msg, e)
        throw new ValidationException(msg)
    }

    (instanceClass, instance)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy