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; 017 018import static java.util.Optional.ofNullable; 019 020import java.lang.annotation.Annotation; 021import java.lang.reflect.Method; 022import java.util.Objects; 023import java.util.Optional; 024import java.util.function.Predicate; 025import java.util.stream.Stream; 026 027import org.talend.sdk.component.api.component.Components; 028import org.talend.sdk.component.api.input.Emitter; 029import org.talend.sdk.component.api.input.PartitionMapper; 030import org.talend.sdk.component.api.processor.Processor; 031import org.talend.sdk.component.api.standalone.DriverRunner; 032 033import lombok.RequiredArgsConstructor; 034 035public class ComponentHelper { 036 037 public static Stream<Class<? extends Annotation>> componentMarkers() { 038 return Stream.of(PartitionMapper.class, Processor.class, Emitter.class, DriverRunner.class); 039 } 040 041 public static Class<?> findPackageOrFail(final Class<?> component, final Predicate<Class<?>> tester, 042 final String api) { 043 final ClassLoader loader = Thread.currentThread().getContextClassLoader(); 044 final String pck = component.getPackage() == null ? null : component.getPackage().getName(); 045 if (pck != null) { 046 String currentPackage = pck; 047 do { 048 try { 049 final Class<?> pckInfo = loader.loadClass(currentPackage + ".package-info"); 050 if (tester.test(pckInfo)) { 051 return pckInfo; 052 } 053 } catch (final ClassNotFoundException e) { 054 // no-op 055 } 056 057 final int endPreviousPackage = currentPackage.lastIndexOf('.'); 058 if (endPreviousPackage < 0) { // we don't accept default package since it is not specific enough 059 break; 060 } 061 062 currentPackage = currentPackage.substring(0, endPreviousPackage); 063 } while (true); 064 } 065 throw new IllegalArgumentException("No @" + api + " for the component " + component 066 + ", add it in package-info.java or disable this validation" 067 + " (which can have side effects in integrations/designers)"); 068 } 069 070 public static Optional<Component> components(final Class<?> component) { 071 return ComponentHelper 072 .componentMarkers() 073 .map(component::getAnnotation) 074 .filter(Objects::nonNull) 075 .findFirst() 076 .map(ComponentHelper::asComponent); 077 } 078 079 public static String findFamily(final Component c, final Class<?> pckMarker) { 080 return ofNullable(c) 081 .map(Component::family) 082 .filter(name -> !name.isEmpty()) 083 .orElseGet(() -> ComponentHelper 084 .findPackageOrFail(pckMarker, (Class<?> clazz) -> clazz.isAnnotationPresent(Components.class), 085 Components.class.getName()) 086 .getAnnotation(Components.class) 087 .family()); 088 } 089 090 public static Component asComponent(final Annotation a) { 091 final String family = ComponentHelper.get(a, "family"); 092 final String name = ComponentHelper.get(a, "name"); 093 094 return new Component(family, name); 095 } 096 097 private static String get(final Annotation a, final String methodName) { 098 try { 099 final Method getter = a.annotationType().getMethod(methodName); 100 return (String) getter.invoke(a); 101 } catch (ReflectiveOperationException ex) { 102 throw new RuntimeException("Can' find " + methodName + " on annotation " + a.annotationType().getName(), 103 ex); 104 } 105 } 106 107 @RequiredArgsConstructor 108 public static class Component { 109 110 private final String family; 111 112 private final String name; 113 114 public String family() { 115 return family; 116 } 117 118 public String name() { 119 return name; 120 } 121 } 122}