1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.util;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.net.MalformedURLException;
23 import java.net.URL;
24 import java.util.HashMap;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.fs.FileStatus;
30 import org.apache.hadoop.fs.FileSystem;
31 import org.apache.hadoop.fs.Path;
32 import org.apache.hadoop.hbase.classification.InterfaceAudience;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 @InterfaceAudience.Private
59 public class DynamicClassLoader extends ClassLoaderBase {
60 private static final Log LOG =
61 LogFactory.getLog(DynamicClassLoader.class);
62
63
64 private static final String DYNAMIC_JARS_DIR = File.separator
65 + "jars" + File.separator;
66
67 private static final String DYNAMIC_JARS_DIR_KEY = "hbase.dynamic.jars.dir";
68
69 private static final String DYNAMIC_JARS_OPTIONAL_CONF_KEY = "hbase.use.dynamic.jars";
70 private static final boolean DYNAMIC_JARS_OPTIONAL_DEFAULT = true;
71
72
73 private final boolean userConfigUseDynamicJars;
74
75 private final boolean useDynamicJars;
76
77 private File localDir;
78
79
80 private FileSystem remoteDirFs;
81 private Path remoteDir;
82
83
84 private HashMap<String, Long> jarModifiedTime;
85
86
87
88
89
90
91
92
93 public DynamicClassLoader(final Configuration conf, final ClassLoader parent) {
94 super(parent);
95
96
97 userConfigUseDynamicJars = conf.getBoolean(
98 DYNAMIC_JARS_OPTIONAL_CONF_KEY, DYNAMIC_JARS_OPTIONAL_DEFAULT);
99
100 boolean dynamicJarsEnabled = userConfigUseDynamicJars;
101 if (dynamicJarsEnabled) {
102 try {
103 initTempDir(conf);
104 dynamicJarsEnabled = true;
105 } catch (Exception e) {
106 LOG.error("Disabling the DynamicClassLoader as it failed to initialize its temp directory."
107 + " Check your configuration and filesystem permissions. Custom coprocessor code may"
108 + " not be loaded as a result of this failure.", e);
109 dynamicJarsEnabled = false;
110 }
111 }
112 useDynamicJars = dynamicJarsEnabled;
113 }
114
115
116
117 private synchronized void initTempDir(final Configuration conf) {
118 jarModifiedTime = new HashMap<>();
119 String localDirPath = conf.get(
120 LOCAL_DIR_KEY, DEFAULT_LOCAL_DIR) + DYNAMIC_JARS_DIR;
121 localDir = new File(localDirPath);
122 if (!localDir.mkdirs() && !localDir.isDirectory()) {
123 throw new RuntimeException("Failed to create local dir " + localDir.getPath()
124 + ", DynamicClassLoader failed to init");
125 }
126
127 String remotePath = conf.get(DYNAMIC_JARS_DIR_KEY);
128 if (remotePath == null || remotePath.equals(localDirPath)) {
129 remoteDir = null;
130 } else {
131 remoteDir = new Path(remotePath);
132 try {
133 remoteDirFs = remoteDir.getFileSystem(conf);
134 } catch (IOException ioe) {
135 LOG.warn("Failed to identify the fs of dir "
136 + remoteDir + ", ignored", ioe);
137 remoteDir = null;
138 }
139 }
140 }
141
142 @Override
143 public Class<?> loadClass(String name)
144 throws ClassNotFoundException {
145 try {
146 return parent.loadClass(name);
147 } catch (ClassNotFoundException e) {
148 if (useDynamicJars) {
149 LOG.debug("Class " + name + " not found - using dynamical class loader");
150 return tryRefreshClass(name);
151 } else if (userConfigUseDynamicJars) {
152
153 LOG.debug("Not checking DynamicClassLoader for missing class because it is disabled."
154 + " See the log for previous errors.");
155 }
156 throw e;
157 }
158 }
159
160 private Class<?> tryRefreshClass(String name) throws ClassNotFoundException {
161 synchronized (getClassLoadingLock(name)) {
162
163 Class<?> clasz = findLoadedClass(name);
164
165 if (clasz != null) {
166 if (LOG.isDebugEnabled()) {
167 LOG.debug("Class " + name + " already loaded");
168 }
169 } else {
170 try {
171 if (LOG.isDebugEnabled()) {
172 LOG.debug("Finding class: " + name);
173 }
174
175 clasz = findClass(name);
176 } catch (ClassNotFoundException cnfe) {
177
178 if (LOG.isDebugEnabled()) {
179 LOG.debug("Loading new jar files, if any");
180 }
181
182 loadNewJars();
183
184 if (LOG.isDebugEnabled()) {
185 LOG.debug("Finding class again: " + name);
186 }
187
188 clasz = findClass(name);
189 }
190 }
191
192 return clasz;
193 }
194 }
195
196 private synchronized void loadNewJars() {
197
198 File[] files = localDir == null ? null : localDir.listFiles();
199 if (files != null) {
200 for (File file : files) {
201 String fileName = file.getName();
202 if (jarModifiedTime.containsKey(fileName)) {
203 continue;
204 }
205 if (file.isFile() && fileName.endsWith(".jar")) {
206 jarModifiedTime.put(fileName, file.lastModified());
207 try {
208 URL url = file.toURI().toURL();
209 addURL(url);
210 } catch (MalformedURLException mue) {
211
212 LOG.warn("Failed to load new jar " + fileName, mue);
213 }
214 }
215 }
216 }
217
218
219 FileStatus[] statuses = null;
220 if (remoteDir != null) {
221 try {
222 statuses = remoteDirFs.listStatus(remoteDir);
223 } catch (IOException ioe) {
224 LOG.warn("Failed to check remote dir status " + remoteDir, ioe);
225 }
226 }
227 if (statuses == null || statuses.length == 0) {
228 return;
229 }
230
231 for (FileStatus status: statuses) {
232 if (status.isDirectory()) {
233 continue;
234 }
235
236 Path path = status.getPath();
237 String fileName = path.getName();
238 if (!fileName.endsWith(".jar")) {
239 if (LOG.isDebugEnabled()) {
240 LOG.debug("Ignored non-jar file " + fileName);
241 }
242 continue;
243 }
244 Long cachedLastModificationTime = jarModifiedTime.get(fileName);
245 if (cachedLastModificationTime != null) {
246 long lastModified = status.getModificationTime();
247 if (lastModified < cachedLastModificationTime) {
248
249
250
251
252
253
254
255 continue;
256 }
257 }
258 try {
259
260 File dst = new File(localDir, fileName);
261 remoteDirFs.copyToLocalFile(path, new Path(dst.getPath()));
262 jarModifiedTime.put(fileName, dst.lastModified());
263 URL url = dst.toURI().toURL();
264 addURL(url);
265 } catch (IOException ioe) {
266 LOG.warn("Failed to load new jar " + fileName, ioe);
267 }
268 }
269 }
270 }