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 }