001/** 002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.talend.sdk.component.tools.validator; 017 018import java.lang.reflect.Method; 019import java.util.Arrays; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.stream.Collectors; 027import java.util.stream.Stream; 028 029import javax.annotation.PostConstruct; 030 031import org.apache.xbean.finder.AnnotationFinder; 032import org.talend.sdk.component.api.configuration.Option; 033import org.talend.sdk.component.api.input.Emitter; 034import org.talend.sdk.component.api.input.PartitionMapper; 035 036/** 037 * Check option parameters on {@link PostConstruct} methods in {@link Emitter} class. 038 */ 039public class OptionParameterValidator implements Validator { 040 041 private static final Map<String, Set<Class<?>>> ALLOWED_OPTION_PARAMETERS = new HashMap<>(); 042 043 static { 044 ALLOWED_OPTION_PARAMETERS.put(Option.MAX_DURATION_PARAMETER, 045 new HashSet<>(Arrays.asList(int.class, Integer.class, long.class, Long.class))); 046 ALLOWED_OPTION_PARAMETERS.put(Option.MAX_RECORDS_PARAMETER, 047 new HashSet<>(Arrays.asList(int.class, Integer.class, long.class, Long.class))); 048 } 049 050 @Override 051 public Stream<String> validate(final AnnotationFinder finder, final List<Class<?>> components) { 052 final Set<Class<?>> emitterClassesOfPartition = finder.findAnnotatedMethods(Emitter.class) 053 .stream() 054 .filter(m -> m.getDeclaringClass().isAnnotationPresent(PartitionMapper.class)) 055 .map(Method::getReturnType) 056 .collect(Collectors.toSet()); 057 058 return finder.findAnnotatedMethods(PostConstruct.class) 059 .stream() 060 .filter(m -> m.getParameterCount() != 0) 061 .filter(m -> emitterClassesOfPartition.contains(m.getDeclaringClass()) 062 || m.getDeclaringClass().isAnnotationPresent(Emitter.class)) 063 .flatMap(m -> Stream.concat( 064 // check that the parameter has Option annotation 065 Arrays.stream(m.getParameters()) 066 .filter(p -> !p.isAnnotationPresent(Option.class)) 067 .map(p -> "Parameter '" + p.getName() 068 + "' should be either annotated with @Option or removed"), 069 Stream.concat( 070 // check option value name 071 Arrays.stream(m.getParameters()) 072 .filter(p -> p.isAnnotationPresent(Option.class)) 073 .filter(p -> !ALLOWED_OPTION_PARAMETERS 074 .containsKey(p.getAnnotation(Option.class).value())) 075 .map(p -> "Option value on the parameter '" + p.getName() 076 + "' is not acceptable. " 077 + "Acceptable values: " + acceptableOptionValues()), 078 // check option parameters' type 079 Arrays.stream(m.getParameters()) 080 .filter(p -> p.isAnnotationPresent(Option.class)) 081 .filter(p -> ALLOWED_OPTION_PARAMETERS 082 .containsKey(p.getAnnotation(Option.class).value())) 083 .filter(p -> !ALLOWED_OPTION_PARAMETERS 084 .get(p.getAnnotation(Option.class).value()) 085 .contains(p.getType())) 086 .map(p -> "The '" + p.getName() + "' parameter's type is not acceptable. " 087 + "Acceptable types: " 088 + acceptableTypeValues(p.getAnnotation(Option.class).value()))))) 089 .sorted(); 090 } 091 092 private static String acceptableOptionValues() { 093 return ALLOWED_OPTION_PARAMETERS.keySet() 094 .stream() 095 .sorted() 096 .collect(Collectors.joining(",", "[", "]")); 097 } 098 099 private static String acceptableTypeValues(final String optionValue) { 100 return ALLOWED_OPTION_PARAMETERS.getOrDefault(optionValue, Collections.emptySet()) 101 .stream() 102 .map(Class::getSimpleName) 103 .sorted() 104 .collect(Collectors.joining(",", "[", "]")); 105 } 106}