001/** 002 * Copyright (C) 2006-2024 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.io.BufferedInputStream; 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.List; 024import java.util.Locale; 025import java.util.Objects; 026import java.util.Optional; 027import java.util.Properties; 028import java.util.stream.Stream; 029 030import org.apache.xbean.finder.AnnotationFinder; 031import org.talend.sdk.component.api.configuration.ui.DefaultValue; 032import org.talend.sdk.component.tools.ComponentValidator.Configuration; 033import org.talend.sdk.component.tools.validator.Validators.ValidatorHelper; 034 035public class LocalConfigurationValidator implements Validator { 036 037 private final Validators.ValidatorHelper helper; 038 039 private final Configuration configuration; 040 041 public LocalConfigurationValidator(final ValidatorHelper helper, final Configuration configuration) { 042 this.helper = helper; 043 this.configuration = configuration; 044 } 045 046 @Override 047 public Stream<String> validate(final AnnotationFinder finder, final List<Class<?>> components) { 048 final String pluginId = this.pluginId(); 049 Stream<String> errStart = helper 050 .componentClassFiles() // 051 .map(root -> new File(root, "TALEND-INF/local-configuration.properties")) // 052 .filter(File::exists) // 053 .flatMap(props -> { 054 final Properties properties = new Properties(); 055 try (final InputStream stream = new BufferedInputStream(new FileInputStream(props))) { 056 properties.load(stream); 057 } catch (final IOException e) { 058 throw new IllegalStateException(e); 059 } 060 return properties 061 .stringPropertyNames() 062 .stream() 063 .filter(it -> !it.toLowerCase(Locale.ROOT).startsWith(pluginId + ".")) 064 .map(it -> "'" + it + "' does not start with '" + pluginId + "', " 065 + "it is recommended to prefix all keys by the family"); 066 }); 067 068 // then check the @DefaultValue annotation 069 Stream<String> familyName = Stream 070 .concat(finder.findAnnotatedFields(DefaultValue.class).stream(), 071 finder.findAnnotatedConstructorParameters(DefaultValue.class).stream()) 072 .map(d -> { 073 final DefaultValue annotation = d.getAnnotation(DefaultValue.class); 074 if (annotation.value().startsWith("local_configuration:") && !annotation 075 .value() 076 .toLowerCase(Locale.ROOT) 077 .startsWith("local_configuration:" + pluginId + ".")) { 078 return d + " does not start with family name (followed by a dot): '" + pluginId + "'"; 079 } 080 return null; 081 }) 082 .filter(Objects::nonNull); 083 084 return Stream.concat(errStart, familyName); 085 } 086 087 private String pluginId() { 088 return Optional.of(configuration).map(Configuration::getPluginId).orElseGet(this::guessPluginId); 089 } 090 091 private String guessPluginId() { // assume folder name == module id 092 return this.helper.componentClassFiles().map(f -> { 093 if (!f.isDirectory()) { 094 return null; 095 } 096 File current = f; 097 int iteration = 5; 098 while (iteration-- > 0 && current != null) { 099 final File currentRef = current; 100 if (Stream 101 .of("classes", "target", "main", "java", "build") 102 .anyMatch(it -> it.equals(currentRef.getName()))) { 103 current = current.getParentFile(); 104 } else { 105 return current.getName(); 106 } 107 } 108 return null; 109 }) // 110 .filter(Objects::nonNull) 111 .findFirst() // 112 .orElseThrow(() -> new IllegalArgumentException("No pluginId set")); 113 } 114}