/*
* Copyright 2014 Avanza Bank AB
*
* 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 com.avanza.astrix.serviceunit;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import com.avanza.astrix.beans.publish.ApiProviderClass;
import com.avanza.astrix.beans.service.ServiceComponent;
import com.avanza.astrix.beans.service.ServiceComponentRegistry;
import com.avanza.astrix.beans.service.ServiceDefinition;
import com.avanza.astrix.provider.core.AstrixServiceExport;
/**
* Server side component used to export all services provided by a given server as defined by
* an {@link AstrixApplicationDescriptor}. <p>
*
* @author Elias Lindholm (elilin)
*
*/
class ServiceExporterImpl implements ServiceExporter {
private final ServiceComponentRegistry serviceComponents;
private final AstrixApplicationDescriptor applicationDescriptor;
private final Collection<ExportedServiceBeanDefinition<?>> exportedServices = new CopyOnWriteArrayList<>();
private final ServiceRegistryExporter serviceRegistryExporter;
private final ConcurrentMap<Class<?>, Object> serviceProviderByType = new ConcurrentHashMap<>();
public ServiceExporterImpl(ServiceComponentRegistry serviceComponents, ServiceRegistryExporter serviceRegistryExporter, ServiceProviderPlugins serviceProviderPlugins, AstrixApplicationDescriptor applicationDescriptor) {
this.serviceComponents = serviceComponents;
this.serviceRegistryExporter = serviceRegistryExporter;
this.applicationDescriptor = applicationDescriptor;
for (ApiProviderClass api : applicationDescriptor.exportsRemoteServicesFor()) {
this.exportedServices.addAll(serviceProviderPlugins.getExportedServices(api));
}
}
public void addServiceProvider(Object bean) {
if (!bean.getClass().isAnnotationPresent(AstrixServiceExport.class)) {
throw new IllegalArgumentException("Service provider beans must be annotated with @AstrixServiceExport. bean: " + bean.getClass().getName());
}
AstrixServiceExport serviceExport = bean.getClass().getAnnotation(AstrixServiceExport.class);
for (Class<?> providedServiceType : serviceExport.value()) {
Object previousProvider = serviceProviderByType.putIfAbsent(providedServiceType, bean);
if (previousProvider != null) {
throw new IllegalStateException(String.format("Multiple providers for same serviceApi detected. serviceApi=%s beanA=%s beanB=%s",
providedServiceType.getName(),
previousProvider.getClass().getName(),
bean.getClass().getName()));
}
}
}
@Override
public void exportService(ExportedServiceBeanDefinition<?> definition) {
this.exportedServices.add(definition);
}
private void exportProvidedServices() {
for (ExportedServiceBeanDefinition<?> serviceBeanDefintion : exportedServices) {
export(serviceBeanDefintion);
}
}
private <T> void export(ExportedServiceBeanDefinition<T> exportedServiceBeanDefinition) {
ServiceDefinition<T> serviceDefinition = exportedServiceBeanDefinition.getServiceDefinition();
ServiceComponent serviceComponent = getServiceComponent(exportedServiceBeanDefinition);
Object provider = null;
if (serviceComponent.requiresProviderInstance()) {
provider = getProvider(exportedServiceBeanDefinition);
}
exportService(exportedServiceBeanDefinition.getBeanType(), provider, serviceDefinition, serviceComponent);
if (exportedServiceBeanDefinition.usesServiceRegistry()) {
serviceRegistryExporter.addExportedService(exportedServiceBeanDefinition, serviceComponent);
}
}
@Override
public void startPublishServices() {
exportProvidedServices();
this.serviceRegistryExporter.startPublishServices();
}
private Object getProvider(ExportedServiceBeanDefinition<?> serviceBeanDefintion) {
Object provider = serviceProviderByType.get(serviceBeanDefintion.getBeanKey().getBeanType());
if (provider == null) {
throw new IllegalStateException(String.format(
"Couldn't find service provider (@AstrixServiceExport annotated bean) for " +
"services exported by service descriptor. Missing provider for: %s. Verify that " +
"current ApplicationContext defines a bean providing the given service that is annotated with @AstrixServiceExport",
serviceBeanDefintion.getBeanType().getName()));
}
return provider;
}
private <T> void exportService(Class<T> providedApi, Object provider, ServiceDefinition<T> versioningContext, ServiceComponent serviceComponent) {
serviceComponent.exportService(providedApi, providedApi.cast(provider), versioningContext);
}
private ServiceComponent getServiceComponent(ExportedServiceBeanDefinition<?> serviceBeanDefinition) {
if (serviceBeanDefinition.getComponentName() != null) {
return this.serviceComponents.getComponent(serviceBeanDefinition.getComponentName());
}
return this.serviceComponents.getComponent(applicationDescriptor.getDefaultServiceComponent());
}
}