001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.hadoop.yarn.util; 020 021 import java.io.File; 022 import java.io.FilenameFilter; 023 import java.net.MalformedURLException; 024 import java.net.URL; 025 import java.net.URLClassLoader; 026 import java.util.ArrayList; 027 import java.util.List; 028 029 import org.apache.commons.logging.Log; 030 import org.apache.commons.logging.LogFactory; 031 import org.apache.hadoop.classification.InterfaceAudience.Public; 032 import org.apache.hadoop.classification.InterfaceStability.Unstable; 033 034 import com.google.common.annotations.VisibleForTesting; 035 import com.google.common.base.Splitter; 036 037 /** 038 * A {@link URLClassLoader} for YARN application isolation. Classes from 039 * the application JARs are loaded in preference to the parent loader. 040 */ 041 @Public 042 @Unstable 043 public class ApplicationClassLoader extends URLClassLoader { 044 045 private static final Log LOG = 046 LogFactory.getLog(ApplicationClassLoader.class.getName()); 047 048 private static final FilenameFilter JAR_FILENAME_FILTER = 049 new FilenameFilter() { 050 @Override 051 public boolean accept(File dir, String name) { 052 return name.endsWith(".jar") || name.endsWith(".JAR"); 053 } 054 }; 055 056 private ClassLoader parent; 057 private List<String> systemClasses; 058 059 public ApplicationClassLoader(URL[] urls, ClassLoader parent, 060 List<String> systemClasses) { 061 super(urls, parent); 062 this.parent = parent; 063 if (parent == null) { 064 throw new IllegalArgumentException("No parent classloader!"); 065 } 066 this.systemClasses = systemClasses; 067 } 068 069 public ApplicationClassLoader(String classpath, ClassLoader parent, 070 List<String> systemClasses) throws MalformedURLException { 071 this(constructUrlsFromClasspath(classpath), parent, systemClasses); 072 } 073 074 @VisibleForTesting 075 static URL[] constructUrlsFromClasspath(String classpath) 076 throws MalformedURLException { 077 List<URL> urls = new ArrayList<URL>(); 078 for (String element : Splitter.on(File.pathSeparator).split(classpath)) { 079 if (element.endsWith("/*")) { 080 String dir = element.substring(0, element.length() - 1); 081 File[] files = new File(dir).listFiles(JAR_FILENAME_FILTER); 082 if (files != null) { 083 for (File file : files) { 084 urls.add(file.toURI().toURL()); 085 } 086 } 087 } else { 088 File file = new File(element); 089 if (file.exists()) { 090 urls.add(new File(element).toURI().toURL()); 091 } 092 } 093 } 094 return urls.toArray(new URL[urls.size()]); 095 } 096 097 @Override 098 public URL getResource(String name) { 099 URL url = null; 100 101 if (!isSystemClass(name, systemClasses)) { 102 url= findResource(name); 103 if (url == null && name.startsWith("/")) { 104 if (LOG.isDebugEnabled()) { 105 LOG.debug("Remove leading / off " + name); 106 } 107 url= findResource(name.substring(1)); 108 } 109 } 110 111 if (url == null) { 112 url= parent.getResource(name); 113 } 114 115 if (url != null) { 116 if (LOG.isDebugEnabled()) { 117 LOG.debug("getResource("+name+")=" + url); 118 } 119 } 120 121 return url; 122 } 123 124 @Override 125 public Class<?> loadClass(String name) throws ClassNotFoundException { 126 return this.loadClass(name, false); 127 } 128 129 @Override 130 protected synchronized Class<?> loadClass(String name, boolean resolve) 131 throws ClassNotFoundException { 132 133 if (LOG.isDebugEnabled()) { 134 LOG.debug("Loading class: " + name); 135 } 136 137 Class<?> c = findLoadedClass(name); 138 ClassNotFoundException ex = null; 139 140 if (c == null && !isSystemClass(name, systemClasses)) { 141 // Try to load class from this classloader's URLs. Note that this is like 142 // the servlet spec, not the usual Java 2 behaviour where we ask the 143 // parent to attempt to load first. 144 try { 145 c = findClass(name); 146 if (LOG.isDebugEnabled() && c != null) { 147 LOG.debug("Loaded class: " + name + " "); 148 } 149 } catch (ClassNotFoundException e) { 150 if (LOG.isDebugEnabled()) { 151 LOG.debug(e); 152 } 153 ex = e; 154 } 155 } 156 157 if (c == null) { // try parent 158 c = parent.loadClass(name); 159 if (LOG.isDebugEnabled() && c != null) { 160 LOG.debug("Loaded class from parent: " + name + " "); 161 } 162 } 163 164 if (c == null) { 165 throw ex != null ? ex : new ClassNotFoundException(name); 166 } 167 168 if (resolve) { 169 resolveClass(c); 170 } 171 172 return c; 173 } 174 175 @VisibleForTesting 176 public static boolean isSystemClass(String name, List<String> systemClasses) { 177 if (systemClasses != null) { 178 String canonicalName = name.replace('/', '.'); 179 while (canonicalName.startsWith(".")) { 180 canonicalName=canonicalName.substring(1); 181 } 182 for (String c : systemClasses) { 183 boolean result = true; 184 if (c.startsWith("-")) { 185 c = c.substring(1); 186 result = false; 187 } 188 if (c.endsWith(".") && canonicalName.startsWith(c)) { 189 return result; 190 } else if (canonicalName.equals(c)) { 191 return result; 192 } 193 } 194 } 195 return false; 196 } 197 }