/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.service;
import static java.lang.String.format;
import static org.apache.commons.io.FilenameUtils.getBaseName;
import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage;
import static org.mule.runtime.core.util.ClassUtils.instanciateClass;
import static org.mule.runtime.core.util.ClassUtils.withContextClassLoader;
import static org.mule.runtime.core.util.FileUtils.unzip;
import static org.mule.runtime.api.util.Preconditions.checkArgument;
import static org.mule.runtime.container.api.MuleFoldersUtil.getServicesFolder;
import static org.mule.runtime.container.api.MuleFoldersUtil.getServicesTempFolder;
import org.mule.runtime.api.service.ServiceProvider;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.module.artifact.classloader.ArtifactClassLoader;
import org.mule.runtime.container.api.MuleFoldersUtil;
import org.mule.runtime.module.artifact.classloader.ArtifactClassLoaderFactory;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.io.filefilter.SuffixFileFilter;
/**
* Discovers services artifacts from the {@link MuleFoldersUtil#SERVICES_FOLDER} folder.
*/
public class FileSystemServiceProviderDiscoverer implements ServiceProviderDiscoverer {
private final ArtifactClassLoader apiClassLoader;
private final ArtifactClassLoaderFactory<ServiceDescriptor> serviceClassLoaderFactory;
/**
* Creates a new instance.
*
* @param containerClassLoader container artifact classLoader. Non null.
* @param serviceClassLoaderFactory factory used to create service's classloaders. Non null.
*/
public FileSystemServiceProviderDiscoverer(ArtifactClassLoader containerClassLoader,
ArtifactClassLoaderFactory<ServiceDescriptor> serviceClassLoaderFactory) {
checkArgument(containerClassLoader != null, "containerClassLoader cannot be null");
checkArgument(serviceClassLoaderFactory != null, "serviceClassLoaderFactory cannot be null");
this.apiClassLoader = containerClassLoader;
this.serviceClassLoaderFactory = serviceClassLoaderFactory;
}
@Override
public List<ServiceProvider> discover() throws ServiceResolutionError {
final ServiceDescriptorFactory serviceDescriptorFactory = new ServiceDescriptorFactory();
final List<ServiceDescriptor> serviceDescriptors = new LinkedList<>();
for (String serviceFile : getServicesFolder().list(new SuffixFileFilter(".zip"))) {
final File tempFolder = new File(getServicesTempFolder(), getBaseName(serviceFile));
try {
unzip(new File(getServicesFolder(), serviceFile), tempFolder);
} catch (IOException e) {
throw new ServiceResolutionError("Error processing service ZIP file", e);
}
final ServiceDescriptor serviceDescriptor = serviceDescriptorFactory.create(tempFolder);
serviceDescriptors.add(serviceDescriptor);
}
return createServiceProviders(serviceDescriptors, serviceClassLoaderFactory);
}
private List<ServiceProvider> createServiceProviders(List<ServiceDescriptor> serviceDescriptors,
ArtifactClassLoaderFactory<ServiceDescriptor> serviceClassLoaderFactory)
throws ServiceResolutionError {
List<ServiceProvider> serviceProviders = new LinkedList<>();
for (ServiceDescriptor serviceDescriptor : serviceDescriptors) {
final ArtifactClassLoader serviceClassLoader =
serviceClassLoaderFactory.create(getServiceArtifactId(serviceDescriptor), serviceDescriptor,
apiClassLoader.getClassLoader(), apiClassLoader.getClassLoaderLookupPolicy());
final ServiceProvider serviceProvider =
instantiateServiceProvider(serviceClassLoader.getClassLoader(), serviceDescriptor.getServiceProviderClassName());
serviceProviders.add(serviceProvider);
}
return serviceProviders;
}
private String getServiceArtifactId(ServiceDescriptor serviceDescriptor) {
return "service/" + serviceDescriptor.getName();
}
private ServiceProvider instantiateServiceProvider(ClassLoader classLoader, String className) throws ServiceResolutionError {
Object reflectedObject;
try {
reflectedObject = withContextClassLoader(classLoader, () -> {
try {
return instanciateClass(className);
} catch (Exception e) {
throw new MuleRuntimeException(createStaticMessage("Unable to create service from class: " + className), e);
}
});
} catch (RuntimeException e) {
throw new ServiceResolutionError(e.getMessage());
}
if (!(reflectedObject instanceof ServiceProvider)) {
throw new ServiceResolutionError(format("Provided service class '%s' does not implement '%s'", className,
ServiceProvider.class.getName()));
}
return (ServiceProvider) reflectedObject;
}
}