001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 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 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 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 de.cuioss.portal.restclient; 017 018import static de.cuioss.portal.configuration.impl.producer.ConnectionMetadataProducer.MISSING_BASENAME_MSG; 019import static de.cuioss.tools.collect.CollectionLiterals.mutableList; 020import static de.cuioss.tools.lang.MoreObjects.requireType; 021import static de.cuioss.tools.string.MoreStrings.requireNotEmpty; 022 023import java.io.Closeable; 024import java.lang.reflect.ParameterizedType; 025import java.lang.reflect.Type; 026import java.util.List; 027import java.util.Optional; 028 029import jakarta.enterprise.context.ApplicationScoped; 030import jakarta.enterprise.context.Dependent; 031import jakarta.enterprise.inject.Produces; 032import jakarta.enterprise.inject.spi.InjectionPoint; 033 034import de.cuioss.portal.configuration.impl.producer.ConnectionMetadataProducer; 035import de.cuioss.portal.configuration.util.ConfigurationHelper; 036import de.cuioss.tools.base.Preconditions; 037import de.cuioss.tools.logging.CuiLogger; 038 039/** 040 * Produces a {@link RestClientHolder} to the given service interface. 041 */ 042@ApplicationScoped 043public class RestClientProducer { 044 045 private static final CuiLogger log = new CuiLogger(RestClientProducer.class); 046 047 @Produces 048 @Dependent 049 @PortalRestClient(baseName = "unused") 050 <T extends Closeable> RestClientHolder<T> produceRestClient(final InjectionPoint injectionPoint) { 051 final var annotationMetaData = ConfigurationHelper.resolveAnnotation(injectionPoint, PortalRestClient.class) 052 .orElseThrow(() -> new IllegalArgumentException( 053 "Expected injectionPoint annotated with @PortalRestClient, but was not:" + injectionPoint)); 054 055 var type = requireType(injectionPoint.getType(), ParameterizedType.class); 056 List<Type> arguments = mutableList(type.getActualTypeArguments()); 057 Preconditions.checkArgument(!arguments.isEmpty()); 058 059 @SuppressWarnings("unchecked") 060 var serviceInterface = (Class<T>) arguments.get(0); 061 062 // Basename must be present 063 final var baseName = suffixNameWithDot(requireNotEmpty(annotationMetaData.baseName(), MISSING_BASENAME_MSG)); 064 065 log.debug("Producing DsmlClient for baseName ='{}'", baseName); 066 067 final var failOnInvalidConfiguration = annotationMetaData.failOnInvalidConfiguration(); 068 try { 069 var connectionMetadata = ConnectionMetadataProducer.createConnectionMetadata(baseName, 070 failOnInvalidConfiguration); 071 return new RestClientHolder<>(new CuiRestClientBuilder(resolveCuiLogger(injectionPoint, serviceInterface)) 072 .connectionMetadata(connectionMetadata).build(serviceInterface)); 073 } catch (IllegalArgumentException e) { 074 log.error("Initialization of RestClientHolder failed", e); 075 return new RestClientHolder<>(null); 076 } 077 } 078 079 /** 080 * @param name to be suffixed, must not be null 081 * @return the given name suffixed with a dot 082 */ 083 public static String suffixNameWithDot(final String name) { 084 return name.endsWith(".") ? name : name + "."; 085 } 086 087 private Optional<Class<?>> resolveCallerClass(InjectionPoint ip) { 088 if (null != ip && null != ip.getMember() && null != ip.getMember().getDeclaringClass()) { 089 090 // works only due to @Dependent scope injection point! 091 final Class<?> clazz = ip.getMember().getDeclaringClass(); 092 if (clazz.getName().contains("$Proxy$")) { // checking for the sake of a peaceful mind 093 log.debug("not using class {} as it seems to be a Weld CDI proxy", clazz.getName()); 094 return Optional.empty(); 095 } 096 return Optional.of(clazz); 097 } 098 return Optional.empty(); 099 } 100 101 private CuiLogger resolveCuiLogger(InjectionPoint ip, Class<?> fallback) { 102 final var clazz = resolveCallerClass(ip).orElse(fallback); 103 log.debug("Using logger class: {}", clazz.getName()); 104 return new CuiLogger(clazz); 105 } 106}