
io.micronaut.http.client.scope.ClientScope Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2017-2018 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.http.client.scope;
import io.micronaut.context.BeanContext;
import io.micronaut.context.BeanResolutionContext;
import io.micronaut.context.LifeCycle;
import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.context.exceptions.DependencyInjectionException;
import io.micronaut.context.scope.CustomScope;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.StringUtils;
import io.micronaut.http.client.*;
import io.micronaut.http.client.loadbalance.FixedLoadBalancer;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanIdentifier;
import io.micronaut.inject.ParametrizedProvider;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.runtime.context.scope.refresh.RefreshEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* A scope for injecting {@link io.micronaut.http.client.HttpClient} implementations.
*
* @author Graeme Rocher
* @since 1.0
*/
@Singleton
class ClientScope implements CustomScope, LifeCycle, ApplicationEventListener {
private static final Logger LOG = LoggerFactory.getLogger(ClientScope.class);
private final Map clients = new ConcurrentHashMap<>();
private final LoadBalancerResolver loadBalancerResolver;
private final BeanContext beanContext;
/**
* @param loadBalancerResolver The load balancer resolver
* @param beanContext The bean context
*/
public ClientScope(LoadBalancerResolver loadBalancerResolver, BeanContext beanContext) {
this.loadBalancerResolver = loadBalancerResolver;
this.beanContext = beanContext;
}
@Override
public boolean isRunning() {
return true;
}
@Override
public Class annotationType() {
return Client.class;
}
@Override
public T get(BeanResolutionContext resolutionContext, BeanDefinition beanDefinition, BeanIdentifier identifier, Provider provider) {
BeanResolutionContext.Segment segment = resolutionContext.getPath().currentSegment().orElseThrow(() ->
new IllegalStateException("@Client used in invalid location")
);
Argument argument = segment.getArgument();
AnnotationValue annotation = argument.getAnnotationMetadata().findAnnotation(Client.class).orElse(null);
if (annotation == null) {
throw new DependencyInjectionException(resolutionContext, argument, "ClientScope called for injection point that is not annotated with @Client");
}
if (!HttpClient.class.isAssignableFrom(argument.getType())) {
throw new DependencyInjectionException(resolutionContext, argument, "@Client used on type that is not an HttpClient");
}
if (!(provider instanceof ParametrizedProvider)) {
throw new DependencyInjectionException(resolutionContext, argument, "ClientScope called with invalid bean provider");
}
String value = annotation.getValue(String.class).orElseThrow(() ->
new DependencyInjectionException(resolutionContext, argument, "No value specified for @Client")
);
LoadBalancer loadBalancer = loadBalancerResolver.resolve(value)
.orElseThrow(() ->
new DependencyInjectionException(resolutionContext, argument, "Invalid service reference [" + value + "] specified to @Client")
);
//noinspection unchecked
return (T) clients.computeIfAbsent(new ClientKey(identifier, value), clientKey -> {
HttpClient existingBean = beanContext.findBean(HttpClient.class, Qualifiers.byName(value)).orElse(null);
if (existingBean != null) {
return existingBean;
}
String contextPath = null;
String annotationPath = annotation.get("path", String.class).orElse(null);
if (StringUtils.isNotEmpty(annotationPath)) {
contextPath = annotationPath;
} else if (StringUtils.isNotEmpty(value) && value.startsWith("/")) {
contextPath = value;
} else {
if (loadBalancer instanceof FixedLoadBalancer) {
contextPath = ((FixedLoadBalancer) loadBalancer).getUrl().getPath();
}
}
Class> configurationClass = annotation.get("configuration", Class.class).orElse(HttpClientConfiguration.class);
Object bean = beanContext.getBean(configurationClass);
if (!(bean instanceof HttpClientConfiguration)) {
throw new IllegalStateException("Referenced HTTP client configuration class must be an instance of HttpClientConfiguration for injection point: " + segment);
}
HttpClientConfiguration configuration = (HttpClientConfiguration) bean;
HttpClient httpClient = (HttpClient) ((ParametrizedProvider) provider).get(loadBalancer, configuration, contextPath);
if (httpClient instanceof DefaultHttpClient) {
((DefaultHttpClient) httpClient).setClientIdentifiers(value);
}
return httpClient;
});
}
@Override
public Optional remove(BeanIdentifier identifier) {
return Optional.empty();
}
@Override
public ClientScope stop() {
for (HttpClient httpClient : clients.values()) {
try {
httpClient.close();
} catch (Throwable e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Error shutting down HTTP client: " + e.getMessage(), e);
}
}
}
clients.clear();
return this;
}
@Override
public void onApplicationEvent(RefreshEvent event) {
refresh();
}
/**
* Client key.
*/
private static class ClientKey {
final BeanIdentifier identifier;
final String value;
public ClientKey(BeanIdentifier identifier, String value) {
this.identifier = identifier;
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ClientKey clientKey = (ClientKey) o;
return Objects.equals(identifier, clientKey.identifier) &&
Objects.equals(value, clientKey.value);
}
@Override
public int hashCode() {
return Objects.hash(identifier, value);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy