use of com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.AnnotationMirror in project CommandHelper by EngineHub.
the class ExtensionManager method Cache.
public static void Cache(File extCache, Class... extraClasses) {
// We will only cache on Windows, as Linux doesn't natively lock
// files that are in use. Windows prevents any modification, making
// it harder for server owners on Windows to update the jars.
boolean onWindows = (OSUtils.GetOS() == OSUtils.OS.WINDOWS);
if (!onWindows) {
return;
}
// Create the directory if it doesn't exist.
extCache.mkdirs();
// cleanup wasn't successful on the last run.
for (File f : extCache.listFiles()) {
try {
Files.delete(f.toPath());
} catch (IOException ex) {
Static.getLogger().log(Level.WARNING, "[CommandHelper] Could not delete loose file " + f.getAbsolutePath() + ": " + ex.getMessage());
}
}
// The cache, cd and dcl here will just be thrown away.
// They are only used here for the purposes of discovering what a given
// jar has to offer.
ClassDiscoveryCache cache = new ClassDiscoveryCache(CommandHelperFileLocations.getDefault().getCacheDirectory());
cache.setLogger(Static.getLogger());
DynamicClassLoader dcl = new DynamicClassLoader();
ClassDiscovery cd = new ClassDiscovery();
cd.setClassDiscoveryCache(cache);
cd.addDiscoveryLocation(ClassDiscovery.GetClassContainer(ExtensionManager.class));
for (Class klazz : extraClasses) {
cd.addDiscoveryLocation(ClassDiscovery.GetClassContainer(klazz));
}
// Look in the given locations for jars, add them to our class discovery.
List<File> toProcess = new ArrayList<>();
for (File location : locations) {
toProcess.addAll(getFiles(location));
}
// Load the files into the discovery mechanism.
for (File file : toProcess) {
if (!file.canRead()) {
continue;
}
URL jar;
try {
jar = file.toURI().toURL();
} catch (MalformedURLException ex) {
Static.getLogger().log(Level.SEVERE, null, ex);
continue;
}
dcl.addJar(jar);
cd.addDiscoveryLocation(jar);
}
cd.setDefaultClassLoader(dcl);
// Loop thru the found lifecycles, copy them to the cache using the name
// given in the lifecycle. If more than one jar has the same internal
// name, the filename will be given a number.
Set<File> done = new HashSet<>();
Map<String, Integer> namecount = new HashMap<>();
// use their internal name.
for (ClassMirror<? extends AbstractExtension> extmirror : cd.getClassesWithAnnotationThatExtend(MSExtension.class, AbstractExtension.class)) {
if (extmirror.equals(new ClassMirror<>(AbstractExtension.class))) {
continue;
}
AnnotationMirror plug = extmirror.getAnnotation(MSExtension.class);
URL plugURL = extmirror.getContainer();
// Get the internal name that this extension exposes.
if (plugURL != null && plugURL.getPath().endsWith(".jar")) {
File f;
try {
f = new File(URLDecoder.decode(plugURL.getFile(), "UTF8"));
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(ExtensionManager.class.getName()).log(Level.SEVERE, null, ex);
continue;
}
// Skip extensions that originate from commandhelpercore.
if (plugURL.equals(ClassDiscovery.GetClassContainer(ExtensionManager.class))) {
done.add(f);
continue;
}
// Skip files already processed.
if (done.contains(f)) {
CHLog.GetLogger().Log(CHLog.Tags.EXTENSIONS, LogLevel.WARNING, f.getAbsolutePath() + " contains more than one extension" + " descriptor. Bug someone about it!", Target.UNKNOWN);
continue;
}
done.add(f);
String name = plug.getValue("value").toString();
// lets track and rename them using a number scheme.
if (namecount.containsKey(name.toLowerCase())) {
int i = namecount.get(name.toLowerCase());
name += "-" + i;
namecount.put(name.toLowerCase(), i++);
CHLog.GetLogger().Log(CHLog.Tags.EXTENSIONS, LogLevel.WARNING, f.getAbsolutePath() + " contains a duplicate internally" + " named extension (" + name + "). Bug someone" + " about it!", Target.UNKNOWN);
} else {
namecount.put(name.toLowerCase(), 1);
}
// Rename the jar to use the plugin's internal name and
// copy it into the cache.
File newFile = new File(extCache, name.toLowerCase() + ".jar");
try {
Files.copy(f.toPath(), newFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Static.getLogger().log(Level.SEVERE, "Could not copy '" + f.getName() + "' to cache: " + ex.getMessage());
}
}
}
Set<ClassMirror<?>> classes = cd.getClassesWithAnnotation(api.class);
// Now process @api annotated extensions, ignoring ones already processed.
for (ClassMirror klass : classes) {
URL plugURL = klass.getContainer();
if (plugURL != null && plugURL.getPath().endsWith(".jar")) {
File f;
try {
f = new File(URLDecoder.decode(plugURL.getFile(), "UTF8"));
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(ExtensionManager.class.getName()).log(Level.SEVERE, null, ex);
continue;
}
// Skip files already processed.
if (done.contains(f)) {
continue;
}
// No special processing needed.
if (cd.doesClassExtend(klass, Event.class) || cd.doesClassExtend(klass, Function.class)) {
// We're processing it here instead of above, complain about it.
CHLog.GetLogger().Log(CHLog.Tags.EXTENSIONS, LogLevel.WARNING, f.getAbsolutePath() + " is an old-style extension!" + " Bug the author to update it to the new extension system!", Target.UNKNOWN);
// Only process this file once.
done.add(f);
File newFile = new File(extCache, "oldstyle-" + f.getName());
try {
Files.copy(f.toPath(), newFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Static.getLogger().log(Level.SEVERE, "Could not copy '" + f.getName() + "' to cache: " + ex.getMessage());
}
}
}
}
// Shut down the original dcl to "unlock" the processed jars.
// The cache and cd instances will just fall into oblivion.
dcl.destroy();
// Explicit call. Without this, jar files won't actually get unlocked on
// Windows. Of course, this is hit and miss, but that's fine; we tried.
System.gc();
}
Aggregations