/** * Loads the contents of the given locations into memory. * * @param locations locations to add (of type {@link java.io.File <File>}). * @throws IOException if an I/O error occurred. */ public synchronized void loadAll(List locations) throws IOException { if (locations == null) { return; } Set data = new HashSet(1000); for (Iterator i = locations.iterator(); i.hasNext(); ) { File location = (File) i.next(); ClassRepositoryEntry entry = loadEntry(location); if (entry != null) { data.addAll(entry.getData()); if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Loaded " + data.size() + " classes from " + location); } } } if (!data.isEmpty()) { data.addAll(Arrays.asList(_content)); _content = (String[]) data.toArray(EMPTY_STRING_ARRAY); Arrays.sort(_content); } }
/** Loads all existing repository infos from persisent storage. */ private void preload() { try { File[] files = getWorkingDir().listFiles(new ExtensionFilter(EXT_REPOSITORY)); long now = System.currentTimeMillis(); /** @todo make user configurable */ long delta = 1000 * 60 * 60 * 24 * 15; for (int i = 0; i < files.length; i++) { ClassRepositoryEntry.Info info = loadInfo(files[i]); _infos.add(info); if ((files[i].lastModified() + delta) < now) { // remove repository file not used for a long time if (!files[i].delete()) { Object[] args = {files[i]}; Loggers.IO.l7dlog(Level.INFO, "IMPORT_DELETE_UNUSED_ERR", args, null); } } } } catch (Throwable ex) { _infos.clear(); Loggers.IO.warn("Error preloading the class repository, no import optimizaton available", ex); } }
/** * Loads the contents of the given location into memory. * * <p>If the given location is already registered, both persistent storage and memory will be * updated. If the location denotes a Java archive (JAR) an update will only be performed if the * archive actually changed. * * <p>Directories will always be updated as there is no easy way to detect changes in such a case. * You should perform logic to avoid unnecessary loads in the Plug-in code. * * <p>If no entry exits for the location, a new entry will be generated and its contents loaded * into memory. * * @param location location to add. * @throws IOException if an I/O error occured. */ public synchronized void load(File location) throws IOException { ClassRepositoryEntry entry = loadEntry(location); if (entry == null) { return; } Set data = entry.getData(); Set temp = new HashSet(_content.length + data.size()); temp.addAll(Arrays.asList(_content)); temp.addAll(data); _content = (String[]) temp.toArray(EMPTY_STRING_ARRAY); Arrays.sort(_content); if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Loaded " + data.size() + " classes from " + location); } }
/** * Unloads the given locations from memory. If one of the given locations was not registered, it * will be ignored. * * @param locations locations to unload (of type {@link java.io.File <File>}). * @throws IOException if an I/O error occurred. * @see ClassRepositoryEntry.Info#getLocation */ public synchronized void unloadAll(List locations) throws IOException { Set data = new HashSet(1000); for (Iterator i = locations.iterator(); i.hasNext(); ) { File location = (File) i.next(); if (location.exists()) { ClassRepositoryEntry entry = null; ClassRepositoryEntry.Info template = null; try { template = new ClassRepositoryEntry.Info(location); if (!_infos.contains(template)) { continue; } entry = createEntry(template); data.addAll(entry.getData()); } catch (Exception ex) { ex.printStackTrace(); continue; } // update the info ClassRepositoryEntry.Info info = (ClassRepositoryEntry.Info) _infos.get(_infos.indexOf(template)); info.setLoaded(false); if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug( "ClassRepository: Unloaded " + entry.getData().size() + " entries for " + location); } } } if (!data.isEmpty()) { Set temp = new HashSet(Arrays.asList(_content)); temp.removeAll(data); _content = (String[]) temp.toArray(EMPTY_STRING_ARRAY); Arrays.sort(_content); } }
/** * Unloads the given file from memory. If the given location was not registered, the call we be * safely ignored. * * @param location location to unload. * @throws IOException if an I/O error occurred. * @see ClassRepositoryEntry.Info#getLocation */ public synchronized void unload(File location) throws IOException { ClassRepositoryEntry.Info template = new ClassRepositoryEntry.Info(location); if (!_infos.contains(template)) { return; } ClassRepositoryEntry entry = createEntry(template); Set data = entry.getData(); Set temp = new HashSet(Arrays.asList(_content)); temp.removeAll(data); _content = (String[]) temp.toArray(EMPTY_STRING_ARRAY); Arrays.sort(_content); // update the info ClassRepositoryEntry.Info info = (ClassRepositoryEntry.Info) _infos.get(_infos.indexOf(template)); info.setLoaded(false); if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Unloaded " + data.size() + " entries for " + location); } }
/** * Loads the entry from the given location. * * @param location location where the entry is stored. * @return the entry. Returns <code>null</code> if the entry is up-to-date. * @throws IOException if the entry could not be loaded. */ private ClassRepositoryEntry loadEntry(File location) throws IOException { // either the user has deleted/renamed the .jar since the first // initialization or the project was newly created if (!location.exists()) { if (location.getName().endsWith(".jar") || location.getName().endsWith(".zip")) { // for .jars this always means an error throw new IOException("File not found -- " + location); } if (!location.mkdirs()) { throw new IOException("Directory not found -- " + location); } if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Created new directory: " + location); } } ClassRepositoryEntry.Info template = new ClassRepositoryEntry.Info(location); ClassRepositoryEntry.Info info = template; ClassRepositoryEntry entry = null; // location already registered if (_infos.contains(template)) { info = (ClassRepositoryEntry.Info) _infos.get(_infos.indexOf(template)); if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Already registered: " + info); } // already loaded in memory if (info.isLoaded()) { if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Alread loaded: " + info); } // always rescan directories as the contents might have changed if (location.isDirectory()) { if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Create new: " + info); Loggers.IO.debug("ClassRepository: Save to disk: " + info); } entry = createEntry(info); saveToDisk(entry); } else { if (location.lastModified() == info.getLocation().lastModified()) { if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Up to date: " + info); } // no changes detected, nothing to do return entry; } if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Create new: " + info); Loggers.IO.debug("ClassRepository: Save to disk: " + info); } entry = createEntry(info); saveToDisk(entry); } } else { // always rescan directories as the contents might have changed if (location.isDirectory()) { if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Create new: " + info); Loggers.IO.debug("ClassRepository: Save to disk: " + info); } entry = createEntry(info); saveToDisk(entry); } else { if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Load from disk: " + info); } // load from persistent storage entry = loadFromDisk(new File(getWorkingDir() + File.separator + info.getFilename())); } } } else { _infos.add(template); if (Loggers.IO.isDebugEnabled()) { Loggers.IO.debug("ClassRepository: Create new: " + info); Loggers.IO.debug("ClassRepository: Save to disk: " + info); } entry = createEntry(template); saveToDisk(entry); } info = (ClassRepositoryEntry.Info) _infos.get(_infos.indexOf(template)); info.setLoaded(true); return entry; }
/** * Creates a new entry. * * @param location location of the package to add. * @param path path to add. * @param types the package types. * @param verify if <code>true</code> the package root check will be performed. * @return if <code>verify == true</code> returns <code>true</code> when the given location could * be verified, <code>false</code> if the given location is no package root; otherwise always * returns <code>true</code>. */ private static boolean createEntryImpl( File location, String path, final Set types, boolean verify) { // strip extension path = path.substring(0, path.lastIndexOf('.')); int pos = path.lastIndexOf('$'); // is this an inner class? if (pos > -1) { String className = path.substring(pos + 1); // skip anonymous inner classes if (StringHelper.isNumber(className)) { return true; } path = path.replace('$', '.'); } else if (verify) { try { // we setup a classloader with the given location and check // whether the location is indeed a package root URL[] url = new URL[] {location.toURL()}; ClassLoader loader = new URLClassLoader(url, ClassRepository.class.getClassLoader()); loader.loadClass(path.replace(File.separatorChar, '.')); } catch (ClassNotFoundException ex) { Object[] args = {path, location}; Loggers.IO.l7dlog(Level.WARN, "REPOSITORY_NOT_PACKAGE_ROOT", args, null); return false; } catch (Throwable ex) { return false; } } String typeName = StringHelper.getClassName(path); // HACK skip obfuscated classes // this is necessary to make our import declaration expanding working // as it could easily be, that an identifier reported by the parser // would be wrongly taken as a type name: // // Database d = new Database(); // d.shutdown(); // ^ // 'd' would be reported as an identifier which is perfect but // lead to wrong results as there could be a class 'd.class' for // obfuscated librarys if ((typeName.length() == 1) && Character.isLowerCase(typeName.charAt(0))) { return true; } String packageName = StringHelper.getPackageName(path); // we place this marker in front of every subpackage so we know // where a package starts (purely for the searching facility in // ImportTransformation.java) if (!EMPTY_STRING.equals(packageName)) { types.add(packageName + '#'); } types.add(path); return true; }