/**
 * The FunctionDescriptor describes a particular function instance enough to invoke the function.
 */
public class FunctionDescriptor implements Serializable, Cloneable {
  private static final long serialVersionUID = 5374103983118037242L;

  private static final boolean ALLOW_NAN_INFINITY =
      PropertiesUtils.getBooleanProperty(
          System.getProperties(), "org.teiid.allowNanInfinity", false); // $NON-NLS-1$

  private Class<?>[] types;
  private Class<?> returnType;
  private boolean requiresContext;
  private FunctionMethod method;
  private String
      schema; // TODO: remove me - we need to create a proper schema for udf and system functions
  private boolean hasWrappedArgs;
  private boolean
      calledWithVarArgArrayParam; // TODO: could store this on the function and pass to invoke

  // This is transient as it would be useless to invoke this method in
  // a different VM.  This function descriptor can be used to look up
  // the real VM descriptor for execution.
  private transient Method invocationMethod;

  FunctionDescriptor() {}

  FunctionDescriptor(
      FunctionMethod method,
      Class<?>[] types,
      Class<?> outputType,
      Method invocationMethod,
      boolean requiresContext) {
    this.types = types;
    this.returnType = outputType;
    this.invocationMethod = invocationMethod;
    this.requiresContext = requiresContext;
    this.method = method;
  }

  public Object newInstance() {
    try {
      return invocationMethod.getDeclaringClass().newInstance();
    } catch (InstantiationException e) {
      throw new TeiidRuntimeException(
          QueryPlugin.Event.TEIID30602,
          QueryPlugin.Util.gs(
              QueryPlugin.Event.TEIID30602, method.getName(), method.getInvocationClass()));
    } catch (IllegalAccessException e) {
      throw new TeiidRuntimeException(
          QueryPlugin.Event.TEIID30602,
          QueryPlugin.Util.gs(
              QueryPlugin.Event.TEIID30602, method.getName(), method.getInvocationClass()));
    }
  }

  public void setHasWrappedArgs(boolean hasWrappedArgs) {
    this.hasWrappedArgs = hasWrappedArgs;
  }

  public String getSchema() {
    return schema;
  }

  public void setSchema(String schema) {
    this.schema = schema;
  }

  public String getName() {
    return this.method.getName();
  }

  public String getFullName() {
    if (CoreConstants.SYSTEM_MODEL.equals(this.schema)) {
      return getName();
    }
    return this.schema + AbstractMetadataRecord.NAME_DELIM_CHAR + getName();
  }

  public PushDown getPushdown() {
    return this.method.getPushdown();
  }

  public Class<?>[] getTypes() {
    return this.types;
  }

  public Class<?> getReturnType() {
    return this.returnType;
  }

  Method getInvocationMethod() {
    return this.invocationMethod;
  }

  public boolean requiresContext() {
    return this.requiresContext;
  }

  @Override
  public String toString() {
    StringBuffer str = new StringBuffer(this.method.getName());
    str.append("("); // $NON-NLS-1$
    for (int i = 0; i < types.length; i++) {
      if (types[i] != null) {
        str.append(types[i].getName());
      } else {
        str.append("null"); // $NON-NLS-1$
      }
      if (i < (types.length - 1)) {
        str.append(", "); // $NON-NLS-1$
      }
    }
    str.append(") : "); // $NON-NLS-1$
    if (returnType == null) {
      str.append("null"); // $NON-NLS-1$
    } else {
      str.append(returnType.getName());
    }
    return str.toString();
  }

  public boolean isNullDependent() {
    return !this.method.isNullOnNull();
  }

  public Determinism getDeterministic() {
    return this.method.getDeterminism();
  }

  @Override
  public FunctionDescriptor clone() {
    try {
      return (FunctionDescriptor) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30381, e);
    }
  }

  public FunctionMethod getMethod() {
    return method;
  }

  void setReturnType(Class<?> returnType) {
    this.returnType = returnType;
  }

  /**
   * Invoke the function described in the function descriptor, using the values provided. Return the
   * result of the function.
   *
   * @param values Values that should match 1-to-1 with the types described in the function
   *     descriptor
   * @param context
   * @param functionTarget TODO
   * @param fd Function descriptor describing the name and types of the arguments
   * @return Result of invoking the function
   */
  public Object invokeFunction(Object[] values, CommandContext context, Object functionTarget)
      throws FunctionExecutionException, BlockedException {
    if (!isNullDependent()) {
      for (int i = requiresContext ? 1 : 0; i < values.length; i++) {
        if (values[i] == null) {
          return null;
        }
      }
    }

    // If descriptor is missing invokable method, find this VM's descriptor
    // give name and types from fd
    if (invocationMethod == null) {
      throw new FunctionExecutionException(
          QueryPlugin.Event.TEIID30382,
          QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30382, getFullName()));
    }

    // Invoke the method and return the result
    try {
      if (hasWrappedArgs) {
        for (int i = 0; i < values.length; i++) {
          Object val = values[i];
          if (val != null && types[i] == DataTypeManager.DefaultDataClasses.VARBINARY) {
            values[i] = ((BinaryType) val).getBytesDirect();
          }
        }
      }
      if (method.isVarArgs()) {
        if (calledWithVarArgArrayParam) {
          ArrayImpl av = (ArrayImpl) values[values.length - 1];
          if (av != null) {
            Object[] vals = av.getValues();
            values[values.length - 1] = vals;
            if (hasWrappedArgs
                && types[types.length - 1] == DataTypeManager.DefaultDataClasses.VARBINARY) {
              vals = Arrays.copyOf(vals, vals.length);
              for (int i = 0; i < vals.length; i++) {
                if (vals[i] != null) {
                  vals[i] = ((BinaryType) vals[i]).getBytesDirect();
                }
              }
              values[values.length - 1] = vals;
            }
            Class<?> arrayType = invocationMethod.getParameterTypes()[types.length - 1];
            if (arrayType.getComponentType() != Object.class && vals.getClass() != arrayType) {
              Object varArgs = Array.newInstance(arrayType.getComponentType(), vals.length);
              for (int i = 0; i < vals.length; i++) {
                Array.set(varArgs, i, vals[i]);
              }
              values[values.length - 1] = varArgs;
            }
          }
        } else {
          int i = invocationMethod.getParameterTypes().length;
          Object[] newValues = Arrays.copyOf(values, i);
          Object varArgs = null;
          if (invocationMethod.getParameterTypes()[i - 1].getComponentType() != Object.class) {
            int varArgCount = values.length - i + 1;
            varArgs =
                Array.newInstance(
                    invocationMethod.getParameterTypes()[i - 1].getComponentType(), varArgCount);
            for (int j = 0; j < varArgCount; j++) {
              Array.set(varArgs, j, values[i - 1 + j]);
            }
          } else {
            varArgs = Arrays.copyOfRange(values, i - 1, values.length);
          }
          newValues[i - 1] = varArgs;
          values = newValues;
        }
      }
      Object result = invocationMethod.invoke(functionTarget, values);
      if (context != null
          && getDeterministic().ordinal() <= Determinism.USER_DETERMINISTIC.ordinal()) {
        context.setDeterminismLevel(getDeterministic());
      }
      return importValue(result, getReturnType());
    } catch (ArithmeticException e) {
      throw new FunctionExecutionException(
          QueryPlugin.Event.TEIID30384,
          e,
          QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, getFullName()));
    } catch (InvocationTargetException e) {
      if (e.getTargetException() instanceof BlockedException) {
        throw (BlockedException) e.getTargetException();
      }
      throw new FunctionExecutionException(
          QueryPlugin.Event.TEIID30384,
          e.getTargetException(),
          QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, getFullName()));
    } catch (IllegalAccessException e) {
      throw new FunctionExecutionException(
          QueryPlugin.Event.TEIID30385,
          e,
          QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30385, method.toString()));
    } catch (TransformationException e) {
      throw new FunctionExecutionException(e);
    }
  }

  public static Object importValue(Object result, Class<?> expectedType)
      throws ArithmeticException, TransformationException {
    if (!ALLOW_NAN_INFINITY) {
      if (result instanceof Double) {
        Double floatVal = (Double) result;
        if (Double.isInfinite(floatVal) || Double.isNaN(floatVal)) {
          throw new ArithmeticException("Infinite or invalid result"); // $NON-NLS-1$
        }
      } else if (result instanceof Float) {
        Float floatVal = (Float) result;
        if (Float.isInfinite(floatVal) || Float.isNaN(floatVal)) {
          throw new ArithmeticException("Infinite or invalid result"); // $NON-NLS-1$
        }
      }
    }
    result =
        DataTypeManager.convertToRuntimeType(
            result, expectedType != DataTypeManager.DefaultDataClasses.OBJECT);
    if (expectedType.isArray() && result instanceof ArrayImpl) {
      return result;
    }
    result = DataTypeManager.transformValue(result, expectedType);
    if (result instanceof String) {
      String s = (String) result;
      if (s.length() > DataTypeManager.MAX_STRING_LENGTH) {
        return s.substring(0, DataTypeManager.MAX_STRING_LENGTH);
      }
    }
    return result;
  }

  public boolean isCalledWithVarArgArrayParam() {
    return calledWithVarArgArrayParam;
  }

  public void setCalledWithVarArgArrayParam(boolean calledWithVarArgArrayParam) {
    this.calledWithVarArgArrayParam = calledWithVarArgArrayParam;
  }

  public boolean isSystemFunction(String name) {
    return this.getName().equalsIgnoreCase(name)
        && CoreConstants.SYSTEM_MODEL.equals(this.getSchema());
  }
}
예제 #2
0
/** Represents the runtime state of a vdb that may aggregate several vdbs. */
public class CompositeVDB {

  private static final boolean WIDEN_COMPARISON_TO_STRING =
      PropertiesUtils.getBooleanProperty(
          System.getProperties(), "org.teiid.widenComparisonToString", true); // $NON-NLS-1$

  private VDBMetaData vdb;
  private MetadataStore store;
  private LinkedHashMap<String, VDBResources.Resource> visibilityMap;
  private UDFMetaData udf;
  LinkedHashMap<VDBKey, CompositeVDB> children;
  private MetadataStore[] additionalStores;
  private ConnectorManagerRepository cmr;
  private FunctionTree systemFunctions;
  private boolean metadataloadFinished = false;
  private VDBMetaData mergedVDB;
  private VDBMetaData originalVDB;
  private Collection<Future<?>> tasks = Collections.synchronizedSet(new HashSet<Future<?>>());

  public CompositeVDB(
      VDBMetaData vdb,
      MetadataStore metadataStore,
      LinkedHashMap<String, VDBResources.Resource> visibilityMap,
      UDFMetaData udf,
      FunctionTree systemFunctions,
      ConnectorManagerRepository cmr,
      VDBRepository vdbRepository,
      MetadataStore... additionalStores)
      throws VirtualDatabaseException {
    this.vdb = vdb;
    this.store = metadataStore;
    this.visibilityMap = visibilityMap;
    this.udf = udf;
    this.systemFunctions = systemFunctions;
    this.cmr = cmr;
    this.additionalStores = additionalStores;
    this.mergedVDB = vdb;
    this.originalVDB = vdb;
    buildCompositeState(vdbRepository);
  }

  private static TransformationMetadata buildTransformationMetaData(
      VDBMetaData vdb,
      LinkedHashMap<String, VDBResources.Resource> visibilityMap,
      MetadataStore store,
      UDFMetaData udf,
      FunctionTree systemFunctions,
      MetadataStore[] additionalStores) {
    Collection<FunctionTree> udfs = new ArrayList<FunctionTree>();
    if (udf != null) {
      for (Map.Entry<String, UDFSource> entry : udf.getFunctions().entrySet()) {
        udfs.add(new FunctionTree(entry.getKey(), entry.getValue(), true));
      }
    }

    CompositeMetadataStore compositeStore = new CompositeMetadataStore(store);
    for (MetadataStore s : additionalStores) {
      compositeStore.merge(s);
      for (Schema schema : s.getSchemas().values()) {
        if (!schema.getFunctions().isEmpty()) {
          UDFSource source = new UDFSource(schema.getFunctions().values());
          if (udf != null) {
            source.setClassLoader(udf.getClassLoader());
          }
          udfs.add(new FunctionTree(schema.getName(), source, true));
        }
        if (!schema.getProcedures().isEmpty()) {
          FunctionTree ft = FunctionTree.getFunctionProcedures(schema);
          if (ft != null) {
            udfs.add(ft);
          }
        }
      }
    }

    TransformationMetadata metadata =
        new TransformationMetadata(vdb, compositeStore, visibilityMap, systemFunctions, udfs);
    metadata.setUseOutputNames(false);
    metadata.setWidenComparisonToString(WIDEN_COMPARISON_TO_STRING);
    return metadata;
  }

  public VDBMetaData getVDB() {
    return this.mergedVDB;
  }

  private void buildCompositeState(VDBRepository vdbRepository) throws VirtualDatabaseException {
    if (vdb.getVDBImports().isEmpty()) {
      this.vdb.addAttchment(ConnectorManagerRepository.class, this.cmr);
      return;
    }

    VDBMetaData newMergedVDB = this.vdb.clone();
    ConnectorManagerRepository mergedRepo = this.cmr;
    if (!this.cmr.isShared()) {
      mergedRepo = new ConnectorManagerRepository();
      mergedRepo.getConnectorManagers().putAll(this.cmr.getConnectorManagers());
    }
    newMergedVDB.addAttchment(ConnectorManagerRepository.class, mergedRepo);
    ClassLoader[] toSearch = new ClassLoader[vdb.getVDBImports().size() + 1];
    toSearch[0] = this.vdb.getAttachment(ClassLoader.class);
    this.children = new LinkedHashMap<VDBKey, CompositeVDB>();
    newMergedVDB.setImportedModels(new TreeSet<String>(String.CASE_INSENSITIVE_ORDER));
    int i = 1;
    for (VDBImport vdbImport : vdb.getVDBImports()) {
      CompositeVDB importedVDB =
          vdbRepository.getCompositeVDB(new VDBKey(vdbImport.getName(), vdbImport.getVersion()));
      if (importedVDB == null) {
        throw new VirtualDatabaseException(
            RuntimePlugin.Event.TEIID40083,
            RuntimePlugin.Util.gs(
                RuntimePlugin.Event.TEIID40083,
                vdb.getName(),
                vdb.getVersion(),
                vdbImport.getName(),
                vdbImport.getVersion()));
      }
      VDBMetaData childVDB = importedVDB.getVDB();
      newMergedVDB.getVisibilityOverrides().putAll(childVDB.getVisibilityOverrides());
      toSearch[i++] = childVDB.getAttachment(ClassLoader.class);
      this.children.put(new VDBKey(childVDB.getName(), childVDB.getVersion()), importedVDB);

      if (vdbImport.isImportDataPolicies()) {
        for (DataPolicy dp : importedVDB.getVDB().getDataPolicies()) {
          DataPolicyMetadata role = (DataPolicyMetadata) dp;
          if (newMergedVDB.addDataPolicy(role) != null) {
            throw new VirtualDatabaseException(
                RuntimePlugin.Event.TEIID40084,
                RuntimePlugin.Util.gs(
                    RuntimePlugin.Event.TEIID40084,
                    vdb.getName(),
                    vdb.getVersion(),
                    vdbImport.getName(),
                    vdbImport.getVersion(),
                    role.getName()));
          }
          if (role.isGrantAll()) {
            role.setSchemas(childVDB.getModelMetaDatas().keySet());
          }
        }
      }

      // add models
      for (ModelMetaData m : childVDB.getModelMetaDatas().values()) {
        if (newMergedVDB.addModel(m) != null) {
          throw new VirtualDatabaseException(
              RuntimePlugin.Event.TEIID40085,
              RuntimePlugin.Util.gs(
                  RuntimePlugin.Event.TEIID40085,
                  vdb.getName(),
                  vdb.getVersion(),
                  vdbImport.getName(),
                  vdbImport.getVersion(),
                  m.getName()));
        }
        newMergedVDB.getImportedModels().add(m.getName());
        String visibilityOverride =
            newMergedVDB.getPropertyValue(m.getName() + ".visible"); // $NON-NLS-1$
        if (visibilityOverride != null) {
          boolean visible = Boolean.valueOf(visibilityOverride);
          newMergedVDB.setVisibilityOverride(m.getName(), visible);
        }
      }
      ConnectorManagerRepository childCmr =
          childVDB.getAttachment(ConnectorManagerRepository.class);
      if (childCmr == null) {
        throw new AssertionError("childVdb had not connector manager repository"); // $NON-NLS-1$
      }
      if (!this.cmr.isShared()) {
        for (Map.Entry<String, ConnectorManager> entry :
            childCmr.getConnectorManagers().entrySet()) {
          if (mergedRepo.getConnectorManagers().put(entry.getKey(), entry.getValue()) != null) {
            throw new VirtualDatabaseException(
                RuntimePlugin.Event.TEIID40086,
                RuntimePlugin.Util.gs(
                    RuntimePlugin.Event.TEIID40086,
                    vdb.getName(),
                    vdb.getVersion(),
                    vdbImport.getName(),
                    vdbImport.getVersion(),
                    entry.getKey()));
          }
        }
      }
    }
    if (toSearch[0] != null) {
      CombinedClassLoader ccl = new CombinedClassLoader(toSearch[0].getParent(), toSearch);
      this.mergedVDB.addAttchment(ClassLoader.class, ccl);
    }
    this.mergedVDB = newMergedVDB;
  }

  private UDFMetaData getUDF() {
    UDFMetaData mergedUDF = new UDFMetaData();
    if (this.udf != null) {
      mergedUDF.addFunctions(this.udf);
    }

    for (Schema schema : store.getSchemas().values()) {
      Collection<FunctionMethod> funcs = schema.getFunctions().values();
      mergedUDF.addFunctions(schema.getName(), funcs);
    }

    if (this.cmr != null) {
      // system scoped common source functions
      for (ConnectorManager cm : this.cmr.getConnectorManagers().values()) {
        List<FunctionMethod> funcs = cm.getPushDownFunctions();
        mergedUDF.addFunctions(CoreConstants.SYSTEM_MODEL, funcs);
      }
    }

    if (this.children != null) {
      // udf model functions - also scoped to the model
      for (CompositeVDB child : this.children.values()) {
        UDFMetaData funcs = child.getUDF();
        if (funcs != null) {
          mergedUDF.addFunctions(funcs);
        }
      }
    }
    return mergedUDF;
  }

  /** TODO: we are not checking for collisions here. */
  private LinkedHashMap<String, VDBResources.Resource> getVisibilityMap() {
    if (this.children == null || this.children.isEmpty()) {
      return this.visibilityMap;
    }

    LinkedHashMap<String, VDBResources.Resource> mergedvisibilityMap =
        new LinkedHashMap<String, VDBResources.Resource>();
    for (CompositeVDB child : this.children.values()) {
      LinkedHashMap<String, VDBResources.Resource> vm = child.getVisibilityMap();
      if (vm != null) {
        mergedvisibilityMap.putAll(vm);
      }
    }
    if (this.visibilityMap != null) {
      mergedvisibilityMap.putAll(this.visibilityMap);
    }
    return mergedvisibilityMap;
  }

  private MetadataStore getMetadataStore() {
    return this.store;
  }

  VDBMetaData getOriginalVDB() {
    return originalVDB;
  }

  public void metadataLoadFinished() {
    if (this.metadataloadFinished) {
      return;
    }
    this.metadataloadFinished = true;

    MetadataStore mergedStore = getMetadataStore();
    // the order of the models is important for resolving ddl
    // TODO we might consider not using the intermediate MetadataStore
    List<Schema> schemas = mergedStore.getSchemaList();
    schemas.clear();
    for (ModelMetaData model : this.vdb.getModelMetaDatas().values()) {
      Schema s = mergedStore.getSchema(model.getName());
      if (s != null) {
        schemas.add(s);
      } else {
        mergedStore.getSchemas().remove(model.getName());
      }
    }
    if (this.children != null && !this.children.isEmpty()) {
      for (CompositeVDB child : this.children.values()) {
        MetadataStore childStore = child.getMetadataStore();
        if (childStore != null) {
          mergedStore.merge(childStore);
        }
      }
    }

    TransformationMetadata metadata =
        buildTransformationMetaData(
            mergedVDB,
            getVisibilityMap(),
            mergedStore,
            getUDF(),
            systemFunctions,
            this.additionalStores);
    QueryMetadataInterface qmi = metadata;
    Map<String, String> multiSourceModels =
        MultiSourceMetadataWrapper.getMultiSourceModels(mergedVDB);
    if (multiSourceModels != null && !multiSourceModels.isEmpty()) {
      qmi = new MultiSourceMetadataWrapper(metadata, multiSourceModels);
    }
    mergedVDB.addAttchment(QueryMetadataInterface.class, qmi);
    mergedVDB.addAttchment(TransformationMetadata.class, metadata);
    mergedVDB.addAttchment(MetadataStore.class, mergedStore);
  }

  LinkedHashMap<VDBKey, CompositeVDB> getChildren() {
    return children;
  }

  public Collection<Future<?>> clearTasks() {
    ArrayList<Future<?>> copy = new ArrayList<Future<?>>(tasks);
    tasks.clear();
    return copy;
  }

  public void removeTask(Future<?> future) {
    tasks.remove(future);
  }

  public void addTask(Future<?> future) {
    tasks.add(future);
  }
}