Merge branch '1.4.5-SNAPSHOT' into 1.5.1-SNAPSHOT Conflicts: src/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/99062d85 Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/99062d85 Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/99062d85 Branch: refs/heads/1.6.0-SNAPSHOT Commit: 99062d85a84b06f54f33222d79b0832d8ec59330 Parents: 09ce814 6537d7a Author: Josh Elser <els...@apache.org> Authored: Mon Feb 3 16:24:46 2014 -0500 Committer: Josh Elser <els...@apache.org> Committed: Mon Feb 3 16:24:46 2014 -0500 ---------------------------------------------------------------------- .../accumulo/start/classloader/AccumuloClassLoader.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/accumulo/blob/99062d85/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java ---------------------------------------------------------------------- diff --cc start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java index 478f4bc,0000000..8cfbdcc mode 100644,000000..100644 --- a/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java +++ b/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java @@@ -1,252 -1,0 +1,257 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.accumulo.start.classloader; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * + */ +public class AccumuloClassLoader { + + public static final String CLASSPATH_PROPERTY_NAME = "general.classpaths"; + + public static final String ACCUMULO_CLASSPATH_VALUE = + "$ACCUMULO_CONF_DIR,\n" + + "$ACCUMULO_HOME/lib/[^.].*.jar,\n" + + "$ZOOKEEPER_HOME/zookeeper[^.].*.jar,\n" + + "$HADOOP_CONF_DIR,\n" + + "$HADOOP_PREFIX/[^.].*.jar,\n" + + "$HADOOP_PREFIX/lib/[^.].*.jar,\n" + + "$HADOOP_PREFIX/share/hadoop/common/.*.jar,\n" + + "$HADOOP_PREFIX/share/hadoop/common/lib/.*.jar,\n" + + "$HADOOP_PREFIX/share/hadoop/hdfs/.*.jar,\n" + - "$HADOOP_PREFIX/share/hadoop/mapreduce/.*.jar,\n" ++ "$HADOOP_PREFIX/share/hadoop/mapreduce/.*.jar,\n" + ++ "/usr/lib/hadoop/[^.].*.jar,\n" + ++ "/usr/lib/hadoop/lib/[^.].*.jar,\n" + ++ "/usr/lib/hadoop-hdfs/[^.].*.jar,\n" + ++ "/usr/lib/hadoop-mapreduce/[^.].*.jar,\n" + ++ "/usr/lib/hadoop-yarn/[^.].*.jar,\n" + ; + + private static String SITE_CONF; + + private static URLClassLoader classloader; + + private static Logger log = Logger.getLogger(AccumuloClassLoader.class); + + static { + String configFile = System.getProperty("org.apache.accumulo.config.file", "accumulo-site.xml"); + if (System.getenv("ACCUMULO_CONF_DIR") != null) { + // accumulo conf dir should be set + SITE_CONF = System.getenv("ACCUMULO_CONF_DIR") + "/" + configFile; + } else if (System.getenv("ACCUMULO_HOME") != null) { + // if no accumulo conf dir, try accumulo home default + SITE_CONF = System.getenv("ACCUMULO_HOME") + "/conf/" + configFile; + } else { + SITE_CONF = null; + } + } + + /** + * Parses and XML Document for a property node for a <name> with the value propertyName if it finds one the function return that property's value for its + * <value> node. If not found the function will return null + * + * @param d + * XMLDocument to search through + * @param propertyName + */ + private static String getAccumuloClassPathStrings(Document d, String propertyName) { + NodeList pnodes = d.getElementsByTagName("property"); + for (int i = pnodes.getLength() - 1; i >= 0; i--) { + Element current_property = (Element) pnodes.item(i); + Node cname = current_property.getElementsByTagName("name").item(0); + if (cname != null && cname.getTextContent().compareTo(propertyName) == 0) { + Node cvalue = current_property.getElementsByTagName("value").item(0); + if (cvalue != null) { + return cvalue.getTextContent(); + } + } + } + return null; + } + + /** + * Looks for the site configuration file for Accumulo and if it has a property for propertyName return it otherwise returns defaultValue Should throw an + * exception if the default configuration can not be read; + * + * @param propertyName + * Name of the property to pull + * @param defaultValue + * Value to default to if not found. + * @return site or default class path String + */ + + public static String getAccumuloString(String propertyName, String defaultValue) { + + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + String site_classpath_string = null; + try { + Document site_conf = db.parse(SITE_CONF); + site_classpath_string = getAccumuloClassPathStrings(site_conf, propertyName); + } catch (Exception e) { + /* we don't care because this is optional and we can use defaults */ + } + if (site_classpath_string != null) + return site_classpath_string; + return defaultValue; + } catch (Exception e) { + throw new IllegalStateException("ClassPath Strings Lookup failed", e); + } + } + + /** + * Replace environment variables in the classpath string with their actual value + * + * @param classpath + * @param env + */ + public static String replaceEnvVars(String classpath, Map<String,String> env) { + Pattern envPat = Pattern.compile("\\$[A-Za-z][a-zA-Z0-9_]*"); + Matcher envMatcher = envPat.matcher(classpath); + while (envMatcher.find(0)) { + // name comes after the '$' + String varName = envMatcher.group().substring(1); + String varValue = env.get(varName); + if (varValue == null) { + varValue = ""; + } + classpath = (classpath.substring(0, envMatcher.start()) + varValue + classpath.substring(envMatcher.end())); + envMatcher.reset(classpath); + } + return classpath; + } + + /** + * Populate the list of URLs with the items in the classpath string + * + * @param classpath + * @param urls + * @throws MalformedURLException + */ + private static void addUrl(String classpath, ArrayList<URL> urls) throws MalformedURLException { + classpath = classpath.trim(); + if (classpath.length() == 0) + return; + + classpath = replaceEnvVars(classpath, System.getenv()); + + // Try to make a URI out of the classpath + URI uri = null; + try { + uri = new URI(classpath); + } catch (URISyntaxException e) { + // Not a valid URI + } + + if (null == uri || !uri.isAbsolute() || (null != uri.getScheme() && uri.getScheme().equals("file://"))) { + // Then treat this URI as a File. + // This checks to see if the url string is a dir if it expand and get all jars in that directory + final File extDir = new File(classpath); + if (extDir.isDirectory()) + urls.add(extDir.toURI().toURL()); + else { + if (extDir.getParentFile() != null) { + File[] extJars = extDir.getParentFile().listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.matches("^" + extDir.getName()); + } + }); + if (extJars != null && extJars.length > 0) { + for (File jar : extJars) + urls.add(jar.toURI().toURL()); + } else { + log.debug("ignoring classpath entry " + classpath); + } + } else { + log.debug("ignoring classpath entry " + classpath); + } + } + } else { + urls.add(uri.toURL()); + } + + } + + private static ArrayList<URL> findAccumuloURLs() throws IOException { + String cp = getAccumuloString(AccumuloClassLoader.CLASSPATH_PROPERTY_NAME, AccumuloClassLoader.ACCUMULO_CLASSPATH_VALUE); + if (cp == null) + return new ArrayList<URL>(); + String[] cps = replaceEnvVars(cp, System.getenv()).split(","); + ArrayList<URL> urls = new ArrayList<URL>(); + for (String classpath : cps) { + if (!classpath.startsWith("#")) { + addUrl(classpath, urls); + } + } + return urls; + } + + public static synchronized ClassLoader getClassLoader() throws IOException { + if (classloader == null) { + ArrayList<URL> urls = findAccumuloURLs(); + + ClassLoader parentClassLoader = AccumuloClassLoader.class.getClassLoader(); + + log.debug("Create 2nd tier ClassLoader using URLs: " + urls.toString()); + URLClassLoader aClassLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader) { + @Override + protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + + if (name.startsWith("org.apache.accumulo.start.classloader.vfs")) { + Class<?> c = findLoadedClass(name); + if (c == null) { + try { + // try finding this class here instead of parent + c = findClass(name); + } catch (ClassNotFoundException e) { + + } + } + } + return super.loadClass(name, resolve); + } + }; + classloader = aClassLoader; + } + + return classloader; + } +}