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