/**
* Mule Development Kit
* Copyright 2010-2011 (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
*
* 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 org.mule.devkit.dynamic.api.loader;
import org.mule.api.Capabilities;
import org.mule.api.Capability;
import org.mule.api.ConnectionManager;
import org.mule.devkit.dynamic.api.helper.Classes;
import org.mule.devkit.dynamic.api.helper.Jars;
import org.mule.devkit.dynamic.api.model.Module;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JarLoader extends Loader {
private static final Logger LOGGER = Logger.getLogger(JarLoader.class.getPackage().getName());
private static final String MODULE_CLASS_SUFFIX = "Module";
private static final String CONNECTOR_CLASS_SUFFIX = "Connector";
private static final String CONNECTION_MANAGER_CLASS_SUFFIX = "ConnectionManager";
/**
* @param fileNames
* @return all potential {@link Module} class name among specified `fileNames`
*/
protected final List<String> findPotentialModuleClassNames(final List<String> fileNames) {
final List<String> potentialModuleClassNames = new LinkedList<String>();
for (final String fileName : fileNames) {
if (fileName.endsWith(JarLoader.MODULE_CLASS_SUFFIX+".class") ||
fileName.endsWith(JarLoader.CONNECTOR_CLASS_SUFFIX+".class")) {
potentialModuleClassNames.add(fileName);
}
}
return potentialModuleClassNames;
}
/**
* @param fileNames
* @param classLoader
* @return first module found among `fileNames`
*/
protected final Class<?> findModuleClass(final List<String> fileNames, final ClassLoader classLoader) {
final List<String> potentialModuleClassNames = findPotentialModuleClassNames(fileNames);
if (potentialModuleClassNames.isEmpty()) {
throw new IllegalArgumentException("Failed to find potential Module class among <"+fileNames+">");
}
for (final String potentialModuleClassName : potentialModuleClassNames) {
final String className = extractClassName(potentialModuleClassName);
final Class<?> moduleClass = Classes.loadClass(classLoader, className);
if (moduleClass == null) {
throw new IllegalArgumentException("Failed to load <"+className+">");
}
if (moduleClass.getAnnotation(org.mule.api.annotations.Module.class) == null && moduleClass.getAnnotation(org.mule.api.annotations.Connector.class) == null) {
if (JarLoader.LOGGER.isLoggable(Level.WARNING)) {
JarLoader.LOGGER.log(Level.WARNING, "Skipping invalid module <{0}>", className);
}
continue;
}
return moduleClass;
}
return null;
}
/**
* @param moduleSubClasses
* @return {@link Class} among specified classes having biggest number of parent {@link Class}es
*/
protected final Class<?> findMostSpecificSubClass(final List<Class<?>> moduleSubClasses) {
return Collections.max(moduleSubClasses, new Comparator<Class<?>>() {
@Override
public int compare(final Class<?> class1, final Class<?> class2) {
return Integer.valueOf(Classes.allSuperClasses(class1).size()).compareTo(Classes.allSuperClasses(class2).size());
}
});
}
/**
* @param generatedPackageName
* @param moduleName
* @param capabilities
* @param classLoader
* @return {@link ConnectionManager} for module if any, null otherwise
*/
protected final ConnectionManager<?, ?> loadConnectionManager(final String generatedPackageName, final String moduleName, final Capabilities capabilities, final ClassLoader classLoader) {
if (capabilities.isCapableOf(Capability.CONNECTION_MANAGEMENT_CAPABLE)) {
final String connectionManagerClassName = generatedPackageName+"."+moduleName+JarLoader.CONNECTION_MANAGER_CLASS_SUFFIX;
final Class<?> connectionManagerClass = Classes.loadClass(classLoader, connectionManagerClassName);
if (connectionManagerClass == null) {
throw new IllegalArgumentException("Failed to load ConnectionManager class <"+connectionManagerClassName+">");
}
final ConnectionManager<?, ?> connectionManager = Classes.newInstance(connectionManagerClass);
if (connectionManager == null) {
throw new IllegalArgumentException("Failed to instantiate ConnectionManager class <"+connectionManagerClass.getCanonicalName()+">");
}
return connectionManager;
}
return null;
}
/**
* @param moduleClass
* @param fileNames
* @param classLoader
* @return all {@link Module} sub {@link Class}es
*/
protected final List<Class<?>> findModuleSubClasses(final Class<?> moduleClass, final List<String> fileNames, final URLClassLoader classLoader) {
final String moduleClassSimpleName = moduleClass.getSimpleName();
final List<Class<?>> subClasses = new LinkedList<Class<?>>();
for (final String fileName : fileNames) {
if (fileName.contains(moduleClassSimpleName)) {
final Class<?> clazz = Classes.loadClass(classLoader, extractClassName(fileName));
if (Classes.allSuperClasses(clazz).contains(moduleClass)) {
subClasses.add(clazz);
}
}
}
return subClasses;
}
/**
* @param urls
* @return a {@link Module} representation of first module found in specified `urls`
* @throws IOException
*/
public final Module load(final List<URL> urls) throws IOException {
final URL moduleJar = urls.get(0);
final List<String> allFileNames = Jars.allFileNames(moduleJar);
final URLClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]));
final Class<?> moduleClass = findModuleClass(allFileNames, classLoader);
if (moduleClass == null) {
throw new IllegalArgumentException("Failed to find Module class in <"+moduleJar+">");
}
final List<Class<?>> moduleSubClasses = findModuleSubClasses(moduleClass, allFileNames, classLoader);
final Class<?> mostSpecificSubClass = findMostSpecificSubClass(moduleSubClasses);
final Capabilities module = Classes.newInstance(mostSpecificSubClass);
if (module == null) {
throw new IllegalArgumentException("Failed to instantiate Module class <"+moduleClass.getSimpleName()+">");
}
if (module.isCapableOf(Capability.CONNECTION_MANAGEMENT_CAPABLE)) {
return load(module, loadConnectionManager(mostSpecificSubClass.getPackage().getName(), moduleClass.getSimpleName(), module, classLoader));
} else {
return load(module, null);
}
}
}