/**
* Copyright (C) 2012 JBoss Inc
*
* 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.jboss.dashboard.factory;
import org.jboss.dashboard.Application;
import org.jboss.dashboard.commons.io.DirectoriesScanner;
import org.jboss.dashboard.profiler.Profiler;
import org.jboss.dashboard.profiler.ThreadProfile;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
/**
* Main class for the initialization and lookup of components within the platform.
* TODO: Replace by CDI
*/
public final class Factory {
private static transient org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(Factory.class.getName());
public static final String FACTORY_FILENAME = "factory.cfg";
public static final String FACTORY_EXTENSION = "factory";
public static final String FACTORY_COMMENT_CHAR = "#";
protected ComponentsTree tree = null;
protected List<String> modules = null;
/**
* Gets a factory for the specified configuration dir. This directory must contain a file
* named FACTORY_FILENAME, with a list of relative directory names where configuration trees
* are present.
*
* @param configDir starting directory
* @return a Factory for this directory, or an empty factory if the directory is invalid.
*/
public static Factory getFactory(File configDir) {
Factory factory;
try {
factory = new Factory(configDir);
} catch (IOException e) {
log.error("Error creating the factory. An empty factory will be created. ", e);
return new Factory();
}
return factory;
}
/**
* Get a factory from the specified input stream, equivalent to the typical directory structure.
*
* @param zis Input stream to the zipped Factory config files
* @return a Factory for this zip file, or an empty factory if the zip is invalid.
*/
public static Factory getFactory(ZipInputStream zis) {
Factory factory;
try {
factory = new Factory(zis);
} catch (IOException e) {
log.error("Error creating the factory. An empty factory will be created. ", e);
return new Factory();
}
return factory;
}
protected Factory(File configDir) throws IOException {
if (configDir.isDirectory()) {
init(configDir);
} else {
init(new ZipInputStream(new BufferedInputStream(new FileInputStream(configDir))));
}
}
protected Factory(ZipInputStream zis) throws IOException {
init(zis);
}
protected Factory() {
}
protected synchronized void init(ZipInputStream zis) throws IOException {
ArrayList entriesOrder = new ArrayList();
HashMap entries = new HashMap();
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.isDirectory()) continue;
String entryName = entry.getName().replace('\\', '/');
if (entryName.equals(FACTORY_FILENAME)) {
log.debug("Using config file: zip:/" + entryName);
BufferedReader input = new BufferedReader(new InputStreamReader(zis));
String line;
while ((line = input.readLine()) != null) {
if (!checkModuleName(line)) continue;
entriesOrder.add(line);
}
} else if (entryName.indexOf('/') == -1) {
log.warn("Ignoring entry inside ZIP " + entryName);
} else {
int index = entryName.indexOf('/');
Properties prop = new Properties();
try {
prop.load(zis);
} catch (IOException e) {
log.error("Error processing entry zip:/" + entryName + ". It will be ignored.", e);
continue;
}
String product = entryName.substring(0, index);
String component = entryName.substring(index + 1, entryName.length() - 1 - FACTORY_EXTENSION.length()).replace('/', '.').replace('\\', '.');
Map m = (Map) entries.get(product);
if (m == null) {
m = new HashMap();
entries.put(product, m);
}
m.put(component, prop);
}
}
zis.close();
List descriptorFiles = new ArrayList();
for (int i = 0; i < entriesOrder.size(); i++) {
String product = (String) entriesOrder.get(i);
Map components = (Map) entries.get(product);
if (components == null || components.isEmpty())
continue;
for (Iterator it = components.keySet().iterator(); it.hasNext();) {
String componentName = (String) it.next();
Properties componentProperties = (Properties) components.get(componentName);
descriptorFiles.add(new DescriptorFile(componentName, componentProperties, "zip:/" + product + "/" + componentName.replace('.', '/') + "." + FACTORY_EXTENSION));
}
}
addDescriptorFiles(descriptorFiles);
}
protected synchronized void init(File factoryDir) throws IOException {
log.info("Configuring Factory from directory: " + factoryDir);
File configFile = new File(factoryDir.getAbsolutePath() + "/" + FACTORY_FILENAME);
log.debug("Using config file: " + configFile);
BufferedReader input = new BufferedReader(new FileReader(configFile));
List<DescriptorFile> descriptorFiles = new ArrayList();
modules = new ArrayList<String>();
String moduleName;
while ((moduleName = input.readLine()) != null) {
if (!checkModuleName(moduleName)) continue;
modules.add(moduleName);
// Read the .factory files from the module's JAR file.
List moduleFiles = initModuleFromJar(moduleName);
descriptorFiles.addAll(moduleFiles);
// Read .factory files from the module's factory directory.
File moduleDir = new File(factoryDir.getAbsolutePath() + "/" + moduleName);
if (moduleDir.exists() && moduleDir.isDirectory()) {
log.debug("Adding " + moduleDir + " to the list of tree roots.");
moduleFiles = initModuleFromDir(moduleDir);
descriptorFiles.addAll(moduleFiles);
}
// Read .factory files from the a zip file named "<moduleName>.zip"
File moduleZip = new File(factoryDir.getAbsolutePath() + "/" + moduleName + ".zip");
if (moduleZip.exists() && moduleZip.isFile()) {
log.debug("Adding " + moduleZip + " to the list of tree roots.");
moduleFiles = initModuleFromZip(moduleZip);
descriptorFiles.addAll(moduleFiles);
}
}
input.close();
addDescriptorFiles(descriptorFiles);
}
protected List<DescriptorFile> initModuleFromDir(File moduleDir) throws IOException {
List<DescriptorFile> descriptorFiles = new ArrayList<DescriptorFile>();
DirectoriesScanner scanner = new DirectoriesScanner(FACTORY_EXTENSION);
File[] descriptorsInside = scanner.findFiles(moduleDir);
int preffixLength = moduleDir.getCanonicalPath().length() + 1;
for (int i = 0; i < descriptorsInside.length; i++) {
File file = descriptorsInside[i];
String componentName = file.getCanonicalPath();
componentName = componentName.substring(preffixLength, componentName.length() - 1 - FACTORY_EXTENSION.length());
componentName = componentName.replace('/', '.');
componentName = componentName.replace('\\', '.');
Properties prop = new Properties();
try {
FileInputStream fis = new FileInputStream(file);
prop.load(fis);
fis.close();
descriptorFiles.add(new DescriptorFile(componentName, prop, file.getCanonicalPath()));
} catch (IOException e) {
log.error("Error processing file " + file + ". It will be ignored.", e);
continue;
}
}
return descriptorFiles;
}
protected List<DescriptorFile> initModuleFromZip(File moduleZip) throws IOException {
List<DescriptorFile> descriptorFiles = new ArrayList<DescriptorFile>();
ZipFile zf = new ZipFile(moduleZip);
for (Enumeration en = zf.entries(); en.hasMoreElements();) {
ZipEntry entry = (ZipEntry) en.nextElement();
if (entry.getName().endsWith(FACTORY_EXTENSION) && !entry.isDirectory()) {
InputStream is = zf.getInputStream(entry);
String componentName = entry.getName();
componentName = componentName.substring(0, componentName.length() - 1 - FACTORY_EXTENSION.length());
componentName = componentName.replace('/', '.');
componentName = componentName.replace('\\', '.');
Properties prop = new Properties();
try {
prop.load(is);
is.close();
descriptorFiles.add(new DescriptorFile(componentName, prop, moduleZip + "!" + entry.getName()));
} catch (IOException e) {
log.error("Error processing file " + entry.getName() + " inside " + moduleZip + ". It will be ignored.", e);
continue;
}
}
}
return descriptorFiles;
}
protected List<DescriptorFile> initModuleFromJar(String moduleName) throws IOException {
List<DescriptorFile> descriptorFiles = new ArrayList<DescriptorFile>();
String libPath = Application.lookup().getBaseAppDirectory() + File.separator + "WEB-INF" + File.separator + "lib";
File libDir = new File(libPath);
DirectoriesScanner ds = new DirectoriesScanner("jar");
File[] jars = ds.findFiles(libDir);
for (int i = 0; i < jars.length; i++) {
File jar = jars[i];
if (jar.getName().startsWith(moduleName)) {
List<DescriptorFile> jarDescriptors = initModuleFromZip(jar);
descriptorFiles.addAll(jarDescriptors);
}
}
return descriptorFiles;
}
protected void addDescriptorFiles(List descriptorFiles) {
tree = new ComponentsTree(descriptorFiles);
log.info("Factory configured.");
if (log.isDebugEnabled())
log.debug("Using factory components " + tree);
}
public void destroy() {
getTree().destroy();
}
public List<String> getModules() {
return modules;
}
public ComponentsTree getTree() {
return tree;
}
protected Object doLookup(String path, String relativeToPath) {
if (tree == null)
return null;
try {
return tree.lookup(path, relativeToPath);
} catch (LookupException e) {
log.error("Error: ", e);
return null;
}
}
protected String doGetAlias(String path, String relativeToPath) {
if (tree == null)
return null;
return tree.getAlias(path, relativeToPath);
}
protected void runWork(FactoryWork work, boolean profile) {
boolean contextOwner = !ComponentsContextManager.isContextStarted();
if (contextOwner) ComponentsContextManager.startContext();
ThreadProfile threadProfile = Profiler.lookup().getCurrentThreadProfile();
boolean profileOwner = threadProfile == null;
if (profile && profileOwner) threadProfile = Profiler.lookup().beginThreadProfile();
try {
work.doWork();
} catch (Throwable t) {
log.error("Error: ", t);
} finally {
if (profile && profileOwner) Profiler.lookup().finishThreadProfile(threadProfile);
if (contextOwner) ComponentsContextManager.clearContext();
}
}
public static Object lookup(String path) {
Factory factory = Application.lookup().getGlobalFactory();
if (factory == null) return null;
return factory.doLookup(path, null);
}
public static Object lookup(String path, String relativeToPath) {
return Application.lookup().getGlobalFactory().doLookup(path, relativeToPath);
}
public static String getAlias(String path) {
return Application.lookup().getGlobalFactory().doGetAlias(path, null);
}
public static Object getAlias(String path, String relativeToPath) {
return Application.lookup().getGlobalFactory().doGetAlias(path, relativeToPath);
}
public static void doWork(FactoryWork work) {
Application.lookup().getGlobalFactory().runWork(work, true);
}
public static void doWork(FactoryWork work, boolean profileWork) {
Application.lookup().getGlobalFactory().runWork(work, profileWork);
}
protected boolean checkModuleName(String moduleName) {
return !StringUtils.isEmpty(moduleName) && !moduleName.startsWith(FACTORY_COMMENT_CHAR);
}
}