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