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.hbase.classification.InterfaceAudience;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FileStatus;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
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 File localDir;
70
71
72 private FileSystem remoteDirFs;
73 private Path remoteDir;
74
75
76 private HashMap<String, Long> jarModifiedTime;
77
78
79
80
81
82
83
84
85 public DynamicClassLoader(
86 final Configuration conf, final ClassLoader parent) {
87 super(parent);
88
89 jarModifiedTime = new HashMap<String, Long>();
90 String localDirPath = conf.get(
91 LOCAL_DIR_KEY, DEFAULT_LOCAL_DIR) + DYNAMIC_JARS_DIR;
92 localDir = new File(localDirPath);
93 if (!localDir.mkdirs() && !localDir.isDirectory()) {
94 throw new RuntimeException("Failed to create local dir " + localDir.getPath()
95 + ", DynamicClassLoader failed to init");
96 }
97
98 String remotePath = conf.get(DYNAMIC_JARS_DIR_KEY);
99 if (remotePath == null || remotePath.equals(localDirPath)) {
100 remoteDir = null;
101 } else {
102 remoteDir = new Path(remotePath);
103 try {
104 remoteDirFs = remoteDir.getFileSystem(conf);
105 } catch (IOException ioe) {
106 LOG.warn("Failed to identify the fs of dir "
107 + remoteDir + ", ignored", ioe);
108 remoteDir = null;
109 }
110 }
111 }
112
113 @Override
114 public Class<?> loadClass(String name)
115 throws ClassNotFoundException {
116 try {
117 return parent.loadClass(name);
118 } catch (ClassNotFoundException e) {
119 if (LOG.isDebugEnabled()) {
120 LOG.debug("Class " + name + " not found - using dynamical class loader");
121 }
122
123 synchronized (getClassLoadingLock(name)) {
124
125 Class<?> clasz = findLoadedClass(name);
126 if (clasz != null) {
127 if (LOG.isDebugEnabled()) {
128 LOG.debug("Class " + name + " already loaded");
129 }
130 }
131 else {
132 try {
133 if (LOG.isDebugEnabled()) {
134 LOG.debug("Finding class: " + name);
135 }
136 clasz = findClass(name);
137 } catch (ClassNotFoundException cnfe) {
138
139 if (LOG.isDebugEnabled()) {
140 LOG.debug("Loading new jar files, if any");
141 }
142 loadNewJars();
143
144 if (LOG.isDebugEnabled()) {
145 LOG.debug("Finding class again: " + name);
146 }
147 clasz = findClass(name);
148 }
149 }
150 return clasz;
151 }
152 }
153 }
154
155 private synchronized void loadNewJars() {
156
157 for (File file: localDir.listFiles()) {
158 String fileName = file.getName();
159 if (jarModifiedTime.containsKey(fileName)) {
160 continue;
161 }
162 if (file.isFile() && fileName.endsWith(".jar")) {
163 jarModifiedTime.put(fileName, Long.valueOf(file.lastModified()));
164 try {
165 URL url = file.toURI().toURL();
166 addURL(url);
167 } catch (MalformedURLException mue) {
168
169 LOG.warn("Failed to load new jar " + fileName, mue);
170 }
171 }
172 }
173
174
175 FileStatus[] statuses = null;
176 if (remoteDir != null) {
177 try {
178 statuses = remoteDirFs.listStatus(remoteDir);
179 } catch (IOException ioe) {
180 LOG.warn("Failed to check remote dir status " + remoteDir, ioe);
181 }
182 }
183 if (statuses == null || statuses.length == 0) {
184 return;
185 }
186
187 for (FileStatus status: statuses) {
188 if (status.isDir()) continue;
189 Path path = status.getPath();
190 String fileName = path.getName();
191 if (!fileName.endsWith(".jar")) {
192 if (LOG.isDebugEnabled()) {
193 LOG.debug("Ignored non-jar file " + fileName);
194 }
195 continue;
196 }
197 Long cachedLastModificationTime = jarModifiedTime.get(fileName);
198 if (cachedLastModificationTime != null) {
199 long lastModified = status.getModificationTime();
200 if (lastModified < cachedLastModificationTime.longValue()) {
201
202
203
204
205
206
207
208 continue;
209 }
210 }
211 try {
212
213 File dst = new File(localDir, fileName);
214 remoteDirFs.copyToLocalFile(path, new Path(dst.getPath()));
215 jarModifiedTime.put(fileName, Long.valueOf(dst.lastModified()));
216 URL url = dst.toURI().toURL();
217 addURL(url);
218 } catch (IOException ioe) {
219 LOG.warn("Failed to load new jar " + fileName, ioe);
220 }
221 }
222 }
223 }