package org.etk.kernel.container.configuration; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.servlet.ServletContext; import org.etk.common.logging.Logger; import org.etk.kernel.container.xml.Component; import org.etk.kernel.container.xml.Configuration; import org.etk.kernel.container.xml.Deserializer; public class ConfigurationManagerImpl implements ConfigurationManager { final static public String WAR_CONF_LOCATION = "/WEB-INF"; protected Configuration configurations; // FIXME ThanhVC:: To research do not depend on the Servlet Context. // Find the solution from Jboss MicroContainer private ServletContext scontext; private ClassLoader scontextClassLoader; private String contextPath = null; private boolean validateSchema = true; private final Set<String> profiles; private final static Logger log = Logger.getLogger(ConfigurationManagerImpl.class); /** * The URL the current document being unmarshaller. */ private static final ThreadLocal<URL> currentURL = new ThreadLocal<URL>(); public static URL getCurrentURL() { return currentURL.get(); } public ConfigurationManagerImpl() { this.profiles = Collections.emptySet(); } public ConfigurationManagerImpl(Set<String> profiles) { this.profiles = profiles; } public ConfigurationManagerImpl(ClassLoader loader, Set<String> profiles) { this.scontextClassLoader = loader; this.profiles = profiles; } public Configuration getConfiuration() { return this.configurations; } public void addConfiguration(ServletContext context, String url) throws Exception { if (url == null) return; addConfiguration(context, getURL(context, url)); } public void addConfiguration(String url) throws Exception { if (url == null) return; addConfiguration(getURL(url)); } public void addConfiguration(Collection urls) throws Exception { Iterator i = urls.iterator(); while (i.hasNext()) { URL url = (URL) i.next(); addConfiguration(url); } } public void addConfiguration(URL url) throws Exception { addConfiguration(scontext, url); } public ConfigurationManagerImpl(ServletContext context, Set<String> profiles) { this.scontext = context; this.profiles = profiles; } /** * @param context * @param url * @throws Exception */ private void addConfiguration(ServletContext context, URL url) throws Exception { if (url == null) return; try { contextPath = (new File(url.toString())).getParent() + "/"; contextPath = contextPath.replace("\\\\", "/"); } catch (Exception e) { contextPath = null; } if (currentURL.get() != null) { throw new IllegalStateException("Would not expect that."); } else { currentURL.set(url); } // try { ConfigurationUnmarshaller unmarshaller = new ConfigurationUnmarshaller(profiles); Configuration conf = unmarshaller.unmarshall(url); if (configurations == null) configurations = conf; else configurations.mergeConfiguration(conf); List urls = conf.getImports(); if (urls != null) { for (int i = 0; i < urls.size(); i++) { String uri = (String)urls.get(i); URL urlObject = getURL(uri); if (urlObject != null) { if (LOG_DEBUG) log.info("\timport " + urlObject); // Set the URL of imported file currentURL.set(urlObject); conf = unmarshaller.unmarshall(urlObject); configurations.mergeConfiguration(conf); } else { log.warn("Couldn't process the URL for " + uri + " configuration file ignored "); } } } } catch (Exception ex) { ex.printStackTrace(); log.error("Cannot process the configuration " + url, ex); } finally { currentURL.set(null); } } /** * Recursively import the configuration files * * @param unmarshaller * the unmarshaller used to unmarshall the configuration file to * import * @param conf * the configuration in which we get the list of files to import * @throws Exception * if an exception occurs while loading the files to import */ private void importConf(ConfigurationUnmarshaller unmarshaller, Configuration conf) throws Exception { importConf(unmarshaller, conf, 1); } /** * Recursively import the configuration files * * @param unmarshaller * the unmarshaller used to unmarshall the configuration file to * import * @param conf * the configuration in which we get the list of files to import * @param depth * used to log properly the URL of the file to import * @throws Exception * if an exception occurs while loading the files to import */ private void importConf(ConfigurationUnmarshaller unmarshaller, Configuration conf, int depth) throws Exception { List urls = conf.getImports(); if (urls != null) { StringBuilder prefix = new StringBuilder(depth); for (int i = 0; i < depth; i++) { prefix.append('\t'); } for (int i = 0; i < urls.size(); i++) { String uri = (String) urls.get(i); URL urlObject = getURL(uri); if (urlObject != null) { // Set the URL of imported file currentURL.set(urlObject); conf = unmarshaller.unmarshall(urlObject); configurations.mergeConfiguration(conf); importConf(unmarshaller, conf, depth + 1); } else { log.warn("Couldn't process the URL for " + uri + " configuration file ignored "); } } } } public void processRemoveConfiguration() { if (configurations == null) return; List list = configurations.getRemoveConfiguration(); if (list != null) { for (int i = 0; i < list.size(); i++) { String type = (String) list.get(i); configurations.removeConfiguration(type); } } } public Component getComponent(String service) { return configurations.getComponent(service); } public Component getComponent(Class clazz) throws Exception { return configurations.getComponent(clazz.getName()); } public Collection getComponents() { if (configurations == null) return null; return configurations.getComponents(); } public boolean isValidateSchema() { return validateSchema; } public void setValidateSchema(boolean validateSchema) { this.validateSchema = validateSchema; } public URL getResource(String url, String defaultURL) throws Exception { if (url == null) url = defaultURL; return getResource(url); } public URL getResource(String uri) throws Exception { return getURL(uri); } public InputStream getInputStream(String url, String defaultURL) throws Exception { if (url == null) url = defaultURL; return getInputStream(url); } public InputStream getInputStream(String uri) throws Exception { final URL url = getURL(uri); if (url == null) { throw new IOException( "Resource (" + uri + ") could not be found or the invoker doesn't have adequate privileges to get the resource"); } return url.openStream(); } public URL getURL(String url) throws Exception { return getURL(scontext, url); } private URL getURL(final ServletContext context, String url) throws Exception { if (url == null) { return null; } else if (url.startsWith("jar:")) { final String path = removePrefix("jar:/", url); final ClassLoader cl = Thread.currentThread() .getContextClassLoader(); return cl.getResource(path); } else if (url.startsWith("classpath:")) { final String path = removePrefix("classpath:/", url); final ClassLoader cl = Thread.currentThread() .getContextClassLoader(); return cl.getResource(path); } else if (url.startsWith("war:")) { String path = removePrefix("war:", url); if (context != null) { final String fPath = path; return context.getResource(WAR_CONF_LOCATION + fPath); } if (scontextClassLoader != null) { if (path.startsWith("/")) { // The ClassLoader doesn't support the first "/" path = path.substring(1); } final String fPath = path; return scontextClassLoader.getResource(fPath); } throw new Exception( "unsupport war uri in this configuration service"); } else if (url.startsWith("file:")) { url = resolveFileURL(url); return new URL(url); } else if (url.indexOf(":") < 0 && contextPath != null) { return new URL(contextPath + url.replace('\\', '/')); } return null; } /** * This methods is used to convert the given into a valid url, it will: * <ol> * <li>Resolve variables in the path if they exist</li> * <li>Replace windows path separators with proper separators</li> * <li>Ensure that the path start with file:///</li> * </ol> * , then it will * * @param url * the url to resolve * @return the resolved url */ private String resolveFileURL(String url) { url = Deserializer.resolveVariables(url); // we ensure that we don't have windows path separator in the url url = url.replace('\\', '/'); if (!url.startsWith("file:///")) { // The url is invalid, so we will fix it // it happens when we use a path of type file://${path}, under // linux or mac os the path will start with a '/' so the url // will be correct but under windows we will have something // like C:\ so the first '/' is missing if (url.startsWith("file://")) { // The url is of type file://, so one '/' is missing url = "file:///" + url.substring(7); } else if (url.startsWith("file:/")) { // The url is of type file:/, so two '/' are missing url = "file:///" + url.substring(6); } else { // The url is of type file:, so three '/' are missing url = "file:///" + url.substring(5); } } return url; } public boolean isDefault(String value) { return value == null || value.length() == 0 || "default".equals(value); } protected String removePrefix(String prefix, String url) { return url.substring(prefix.length(), url.length()); } @Override public Configuration getConfiguration() { // TODO Auto-generated method stub return configurations; } }