package org.dtangler.core.dependencyengine;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.dtangler.core.configuration.Arguments;
import org.dtangler.core.exception.DtException;
import org.dtangler.core.filefinder.RecursiveFileFinder;
import org.dtangler.core.input.ConfigFileParser;
public class DependencyEnginePool {
private final List<DependencyEngine> dependencyEngines = new ArrayList<DependencyEngine>();
private final String packagenamePrefix = "org/dtangler";
private final String dependencyEngineConfigFilePrefix = "dependency-engine-";
private final String dependencyEngineConfigFileRegex = ".*"
+ dependencyEngineConfigFilePrefix + ".*\\.properties$";
private final String dependencyEnginePoolConfigFileRegex = ".*"
+ dependencyEngineConfigFilePrefix + "pool\\.properties$";
private String defaultDependencyEngineId;
public DependencyEnginePool() {
initPool();
}
public DependencyEnginePool(DependencyEngine... dependencyEngines) {
if (dependencyEngines != null) {
for (DependencyEngine dependencyEngine : dependencyEngines) {
if (dependencyEngine.getDependencyEngineId() == null)
dependencyEngine.setDependencyEngineId(dependencyEngine.getClass().getSimpleName());
add(dependencyEngine.getDependencyEngineId(), dependencyEngine);
}
}
}
private DependencyEngine getDependencyEngine(String id) {
for (DependencyEngine dependencyEngine : dependencyEngines) {
if (dependencyEngine == null
|| dependencyEngine.getDependencyEngineId() == null)
continue;
if (dependencyEngine.getDependencyEngineId().equalsIgnoreCase(id))
return dependencyEngine;
}
return null;
}
private void addDependencyEngine(DependencyEngine dependencyEngine) {
if (dependencyEngine != null)
dependencyEngines.add(dependencyEngine);
}
public synchronized void add(String id, DependencyEngine dependencyEngine) {
if (id == null || id.length() == 0) {
throw new DtException(
"Unable to load the dependency engine. Id not specified.");
}
if (getDependencyEngine(id) != null) {
throw new DtException(
"Unable to load the dependency engine. Dependency engine with id="
+ id + " already exists.");
}
dependencyEngine.setDependencyEngineId(id);
addDependencyEngine(dependencyEngine);
}
private DependencyEngine findEngine(Arguments arguments) {
DependencyEngine engineMaybe = null;
boolean ambiguousSituation = false;
for (DependencyEngine dependencyEngine : dependencyEngines) {
if (dependencyEngine == null)
continue;
if (dependencyEngine.getArgumentsMatchThisEngine(arguments) == DependencyEngine.ArgumentsMatch.yes)
return dependencyEngine;
if (dependencyEngine.getArgumentsMatchThisEngine(arguments) == DependencyEngine.ArgumentsMatch.maybe) {
if (engineMaybe == null) {
engineMaybe = dependencyEngine;
} else {
ambiguousSituation = true;
}
}
}
if (!ambiguousSituation && engineMaybe != null) {
return engineMaybe;
}
throw new DtException(
"unable to determine the dependency engine to be utilized");
}
public synchronized DependencyEngine get(Arguments arguments) {
DependencyEngine dependencyEngine = null;
try {
dependencyEngine = findEngine(arguments);
} catch (DtException ex) {
dependencyEngine = getDefaultEngine();
}
if (arguments.getDependencyEngineId() != null
&& arguments.getDependencyEngineId().length() > 0) {
if (!(dependencyEngine != null
&& dependencyEngine.getDependencyEngineId() != null && dependencyEngine
.getDependencyEngineId().equalsIgnoreCase(
arguments.getDependencyEngineId()))) {
throw new DtException(
"unable to find a dependency engine with id: "
+ arguments.getDependencyEngineId());
}
};
return dependencyEngine;
}
public DependencyEngine getDefaultEngine() {
if (defaultDependencyEngineId == null)
throw new DtException(
"unable to find the default dependency engine id");
return get(defaultDependencyEngineId);
}
public synchronized DependencyEngine get(String id) {
DependencyEngine engine = getDependencyEngine(id);
if (engine == null)
throw new DtException(
"unable to find a dependency engine with id: " + id);
return engine;
}
public synchronized List<String> getDependencyEngineIds() {
List<String> list = new ArrayList<String>();
for (DependencyEngine dependencyEngine : dependencyEngines) {
if (dependencyEngine == null
|| dependencyEngine.getDependencyEngineId() == null)
continue;
list.add(new String(dependencyEngine.getDependencyEngineId()));
}
return list;
}
private class FileNameDependencyEngineConfig implements FileFilter {
public boolean accept(File file) {
boolean match = false;
if (file.isFile()
&& file.getName().matches(dependencyEngineConfigFileRegex)) {
match = true;
}
return match;
}
}
private InputStream getFileAsInputStream(String fileName) {
if (isJarFileName(fileName)) {
try {
JarURLConnection jarURLConnection = openJarURLConnection(fileName);
JarFile jarFile = jarURLConnection.getJarFile();
return jarFile.getInputStream(jarURLConnection.getJarEntry());
} catch (DtException e) {
throw new RuntimeException("Cannot open file: " + fileName + ": "
+ e.getCause());
} catch (IOException e) {
throw new RuntimeException("Cannot open file: " + fileName + ": "
+ e.getCause());
}
} else {
try {
File file = new File(fileName);
return new FileInputStream(file);
} catch (IOException e) {
throw new RuntimeException("Cannot open file: " + fileName
+ e.getCause());
}
}
}
private void loadDependencyEngine(InputStream inputStream) {
Map<String, String> configFileValues = new ConfigFileParser(
inputStream, DependencyEngineConfigConstants.VALID_KEYS)
.parseValues();
String id = configFileValues
.get(DependencyEngineConfigConstants.ID_DEPENDENCY_ENGINE_KEY);
String dependencyEngineClassPath = configFileValues
.get(DependencyEngineConfigConstants.CLASS_NAME_DEPENDENCY_ENGINE_KEY);
Object dependencyEngine = null;
try {
Class<?> c = Class.forName(dependencyEngineClassPath);
dependencyEngine = c.newInstance();
} catch (Exception e) {
throw new DtException(
"unable to instantiate dependency engine id = " + id
+ ", class name = " + dependencyEngineClassPath);
}
if (dependencyEngine instanceof DependencyEngine) {
add(id, (DependencyEngine) dependencyEngine);
} else {
throw new DtException("invalid dependency engine class: id = " + id
+ ", class name = " + dependencyEngineClassPath
+ ". Dependency engine must implement the interface "
+ DependencyEngine.class.getName());
}
}
private void loadDependencyEnginePoolSettings(InputStream inputStream) {
Map<String, String> configFileValues = new ConfigFileParser(
inputStream, DependencyEnginePoolConfigConstants.VALID_KEYS)
.parseValues();
defaultDependencyEngineId = configFileValues
.get(DependencyEnginePoolConfigConstants.ID_DEFAULT_DEPENDENCY_ENGINE_KEY);
if (defaultDependencyEngineId == null)
throw new DtException(
"unable to determine the default dependency engine id");
}
private void initPool() {
List<String> listDependencyEngineConfigFilenames = searchDependencyEngineConfigFiles(
packagenamePrefix, DependencyEnginePool.class);
for (String configFileName : listDependencyEngineConfigFilenames) {
if (configFileName == null)
continue;
InputStream inputStream = getFileAsInputStream(configFileName);
if (configFileName.matches(dependencyEnginePoolConfigFileRegex)) {
loadDependencyEnginePoolSettings(inputStream);
} else {
loadDependencyEngine(inputStream);
}
}
if (dependencyEngines.isEmpty()) {
throw new RuntimeException("error: no dependency engine found");
}
}
private String getFileName(String fileName) {
if (fileName == null)
return null;
try {
fileName = URLDecoder.decode(fileName, "UTF-8");
if (fileName.indexOf('!') != -1)
fileName = fileName.substring(0, fileName.indexOf('!'));
} catch (Exception e) {
}
if (fileName.length() > 1 && fileName.matches("/.:/.*"))
return fileName.substring(1);
if (fileName.length() > "file:/".length()
&& fileName.matches("file:/.:/.*"))
return fileName.substring("file:/".length());
if (fileName.length() > "file:".length()
&& fileName.matches("file:/.*"))
return fileName.substring("file:".length());
return fileName;
}
private boolean isJarFileName(String fileName) {
if (fileName == null)
throw new DtException("invalid file name: null");
return fileName.lastIndexOf("!") >= 0 ? true : false;
}
private String getJarFileEntryName(String fileName) {
if (fileName == null)
throw new DtException("invalid jar file name: null");
int exclPos = fileName.lastIndexOf("!");
if (exclPos >= 0 && exclPos < fileName.length() - 1) {
return fileName.substring(exclPos + 2);
}
throw new DtException("file " + fileName + " is not a jar file");
}
private List<String> searchDependencyEngineConfigFiles(File file) {
List<String> fileNames = new ArrayList<String>();
if (file == null || file.getAbsolutePath() == null)
return fileNames;
if (file.isDirectory()) {
RecursiveFileFinder fileFinder = new RecursiveFileFinder();
fileFinder.setFilter(new FileNameDependencyEngineConfig());
fileFinder.findFiles(file.getPath());
for (File f : fileFinder.getFiles()) {
if (f == null || f.getAbsolutePath() == null)
continue;
fileNames.add(f.getAbsolutePath());
}
} else {
if (new FileNameDependencyEngineConfig().accept(file)) {
fileNames.add(file.getAbsolutePath());
}
}
return fileNames;
}
private List<String> searchDependencyEngineConfigFiles(
String prefixPackageName, Class<?> contentLoaderClass) {
List<String> fileNames = new ArrayList<String>();
Enumeration<?> enumFiles = findConfigFileUrls(prefixPackageName,
contentLoaderClass);
while (enumFiles != null && enumFiles.hasMoreElements()) {
URL url = (URL) enumFiles.nextElement();
if (url == null || url.getFile() == null
|| url.getFile().length() <= 1)
continue;
String fileName = getFileName(url.getPath());
if (url.getProtocol() != null
&& url.getProtocol().equalsIgnoreCase("jar")) {
String elementInJar = getJarFileEntryName(url.getPath());
JarFile jarFile = openJar(url);
List<JarEntry> entries = Collections.list(jarFile.entries());
for (JarEntry entry : entries) {
String entryName = entry.getName();
if (entryName.startsWith(elementInJar)) {
if (!entry.isDirectory()
&& entryName
.matches(dependencyEngineConfigFileRegex)) {
fileNames.add(fileName + "!" + "/"
+ entry.toString());
}
}
}
} else {
List<String> list = searchDependencyEngineConfigFiles(new File(
fileName));
if (list != null && list.size() > 0) {
fileNames.addAll(list);
}
}
}
return fileNames;
}
private JarFile openJar(URL url) {
try {
JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();
return jarURLConnection.getJarFile();
} catch (IOException e) {
throw new RuntimeException("Cannot open jar file: " + url.getFile(), e);
}
}
private JarURLConnection openJarURLConnection(String fileName) {
try {
URL url = null;
if (fileName.toLowerCase().startsWith("http")) {
url = new URL("jar:"+fileName);
} else {
if (fileName.startsWith("/"))
fileName = fileName.substring(1);
url = new URL("jar:file:/"+fileName);
}
return (JarURLConnection)url.openConnection();
} catch (MalformedURLException e) {
throw new RuntimeException("Cannot open jar file: " + fileName, e);
} catch (IOException e) {
throw new RuntimeException("Cannot open jar file: " + fileName, e);
}
}
private Enumeration<URL> findConfigFileUrls(String prefixPackageName,
Class<?> contentLoaderClass) {
try {
return contentLoaderClass.getClassLoader().getResources(
prefixPackageName);
} catch (IOException e) {
throw new RuntimeException(
"Cannot find config file URLs starting with prefix:"
+ prefixPackageName, e);
}
}
}