src/core/net/sf/basedb/util/extensions/manager/ExtensionsManager.java

Code
Comments
Other
Rev Date Author Line
5598 30 Mar 11 nicklas 1 /**
5598 30 Mar 11 nicklas 2   $Id$
5598 30 Mar 11 nicklas 3
5598 30 Mar 11 nicklas 4   Copyright (C) 2011 Nicklas Nordborg
5598 30 Mar 11 nicklas 5
5598 30 Mar 11 nicklas 6   This file is part of BASE - BioArray Software Environment.
5598 30 Mar 11 nicklas 7   Available at http://base.thep.lu.se/
5598 30 Mar 11 nicklas 8
5598 30 Mar 11 nicklas 9   BASE is free software; you can redistribute it and/or
5598 30 Mar 11 nicklas 10   modify it under the terms of the GNU General Public License
5598 30 Mar 11 nicklas 11   as published by the Free Software Foundation; either version 3
5598 30 Mar 11 nicklas 12   of the License, or (at your option) any later version.
5598 30 Mar 11 nicklas 13
5598 30 Mar 11 nicklas 14   BASE is distributed in the hope that it will be useful,
5598 30 Mar 11 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
5598 30 Mar 11 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
5598 30 Mar 11 nicklas 17   GNU General Public License for more details.
5598 30 Mar 11 nicklas 18
5598 30 Mar 11 nicklas 19   You should have received a copy of the GNU General Public License
5598 30 Mar 11 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
5598 30 Mar 11 nicklas 21 */
5598 30 Mar 11 nicklas 22 package net.sf.basedb.util.extensions.manager;
5598 30 Mar 11 nicklas 23
5598 30 Mar 11 nicklas 24 import java.io.File;
5598 30 Mar 11 nicklas 25 import java.io.FileFilter;
5602 07 Apr 11 nicklas 26 import java.net.URI;
5601 01 Apr 11 nicklas 27 import java.util.ArrayList;
5603 08 Apr 11 nicklas 28 import java.util.Collection;
5605 12 Apr 11 nicklas 29 import java.util.Comparator;
5601 01 Apr 11 nicklas 30 import java.util.HashMap;
5598 30 Mar 11 nicklas 31 import java.util.HashSet;
5603 08 Apr 11 nicklas 32 import java.util.Iterator;
5601 01 Apr 11 nicklas 33 import java.util.List;
5601 01 Apr 11 nicklas 34 import java.util.Map;
5598 30 Mar 11 nicklas 35 import java.util.Set;
5602 07 Apr 11 nicklas 36 import java.util.TreeMap;
7231 16 Nov 16 nicklas 37 import java.util.TreeSet;
5598 30 Mar 11 nicklas 38
5606 14 Apr 11 nicklas 39 import net.sf.basedb.core.ItemAlreadyExistsException;
5598 30 Mar 11 nicklas 40 import net.sf.basedb.util.RegexpFileFilter;
5598 30 Mar 11 nicklas 41 import net.sf.basedb.util.extensions.Registry;
5602 07 Apr 11 nicklas 42 import net.sf.basedb.util.extensions.manager.ExtensionsFile.WriteableExtensionsFile;
5603 08 Apr 11 nicklas 43 import net.sf.basedb.util.filter.Filter;
5598 30 Mar 11 nicklas 44
5598 30 Mar 11 nicklas 45 /**
5598 30 Mar 11 nicklas 46   An extensions manager is responsible for handling a group of 
5598 30 Mar 11 nicklas 47   extensions in the form of files stored in the file system. The 
5598 30 Mar 11 nicklas 48   manager will keep track of added, updated and removed files.
5598 30 Mar 11 nicklas 49
5598 30 Mar 11 nicklas 50   @author Nicklas
5598 30 Mar 11 nicklas 51   @since 3.0
5598 30 Mar 11 nicklas 52   @base.modified $Date$
5598 30 Mar 11 nicklas 53 */
5598 30 Mar 11 nicklas 54 public class ExtensionsManager
5598 30 Mar 11 nicklas 55 {
5598 30 Mar 11 nicklas 56   
6444 09 Apr 14 nicklas 57   private static final org.slf4j.Logger log = 
6444 09 Apr 14 nicklas 58     org.slf4j.LoggerFactory.getLogger(ExtensionsManager.class);
5598 30 Mar 11 nicklas 59
5606 14 Apr 11 nicklas 60   // Main registry for extensions
5598 30 Mar 11 nicklas 61   private final Registry registry;
5606 14 Apr 11 nicklas 62   // Settings file
5606 14 Apr 11 nicklas 63   private final Settings settings;
5598 30 Mar 11 nicklas 64   // Manually added ignore files
5603 08 Apr 11 nicklas 65   private final Set<File> ignore;
5603 08 Apr 11 nicklas 66   // Directories to scan for new/deleted/modified extensions
5603 08 Apr 11 nicklas 67   private final Set<File> directories;
5616 27 Apr 11 nicklas 68   // Extension files
5602 07 Apr 11 nicklas 69   private final Map<URI, ExtensionsFile> xtFiles;
5602 07 Apr 11 nicklas 70   // Map objects to the file they are defined in. The key is a unique object id.
5606 14 Apr 11 nicklas 71   private final Map<ObjectKey<?>, ExtensionsFile> installedObjects;
5601 01 Apr 11 nicklas 72   
6520 18 Aug 14 nicklas 73   private ProcessResults lastResults;
6520 18 Aug 14 nicklas 74   
5603 08 Apr 11 nicklas 75   private int numNew;
5603 08 Apr 11 nicklas 76   private int numModified;
5603 08 Apr 11 nicklas 77   private int numUnmodified;
5603 08 Apr 11 nicklas 78   private int numDeleted;
7231 16 Nov 16 nicklas 79   private int numIgnored;
5603 08 Apr 11 nicklas 80   
5598 30 Mar 11 nicklas 81   /**
5598 30 Mar 11 nicklas 82     Create a new extensions manager and use the given registry
5598 30 Mar 11 nicklas 83     to store extensions.
5598 30 Mar 11 nicklas 84     
5616 27 Apr 11 nicklas 85     @param registry The registry to use
5616 27 Apr 11 nicklas 86     @param settingsFile The file were settings are stored
5598 30 Mar 11 nicklas 87   */
5606 14 Apr 11 nicklas 88   public ExtensionsManager(Registry registry, File settingsFile)
5598 30 Mar 11 nicklas 89   {
5598 30 Mar 11 nicklas 90     this.registry = registry;
5606 14 Apr 11 nicklas 91     this.settings = new Settings(this, settingsFile);
7231 16 Nov 16 nicklas 92     this.ignore = new TreeSet<File>(settings.getIgnoredFiles());
5603 08 Apr 11 nicklas 93     this.directories = new HashSet<File>();
5605 12 Apr 11 nicklas 94     this.xtFiles = new TreeMap<URI, ExtensionsFile>(new JarFirstURIComparator());
5606 14 Apr 11 nicklas 95     this.installedObjects = new HashMap<ObjectKey<?>, ExtensionsFile>();
5606 14 Apr 11 nicklas 96     addIgnore(settingsFile);
5598 30 Mar 11 nicklas 97   }
5598 30 Mar 11 nicklas 98   
5598 30 Mar 11 nicklas 99   /**
5598 30 Mar 11 nicklas 100     Get the registry in which all extensions that are managed by this
5598 30 Mar 11 nicklas 101     manager are stored.
5598 30 Mar 11 nicklas 102   */
5598 30 Mar 11 nicklas 103   public Registry getRegistry()
5598 30 Mar 11 nicklas 104   {
5598 30 Mar 11 nicklas 105     return registry;
5598 30 Mar 11 nicklas 106   }
5598 30 Mar 11 nicklas 107   
5598 30 Mar 11 nicklas 108   /**
5606 14 Apr 11 nicklas 109     Get the settings for the extension manager.
5606 14 Apr 11 nicklas 110   */
5606 14 Apr 11 nicklas 111   public Settings getSettings()
5606 14 Apr 11 nicklas 112   {
5606 14 Apr 11 nicklas 113     return settings;
5606 14 Apr 11 nicklas 114   }
5606 14 Apr 11 nicklas 115   
5606 14 Apr 11 nicklas 116   /**
6520 18 Aug 14 nicklas 117     Save the last process results so that they can be used 
6520 18 Aug 14 nicklas 118     later on.
6520 18 Aug 14 nicklas 119     @since 3.3
6520 18 Aug 14 nicklas 120   */
6520 18 Aug 14 nicklas 121   public void setLastProcessResults(ProcessResults results)
6520 18 Aug 14 nicklas 122   {
6520 18 Aug 14 nicklas 123     this.lastResults = results;
6520 18 Aug 14 nicklas 124   }
6520 18 Aug 14 nicklas 125   
6520 18 Aug 14 nicklas 126   /**
6520 18 Aug 14 nicklas 127     Get the results of the last processing.
6520 18 Aug 14 nicklas 128     @since 3.3
6520 18 Aug 14 nicklas 129   */
6520 18 Aug 14 nicklas 130   public ProcessResults getLastProcessResults()
6520 18 Aug 14 nicklas 131   {
6520 18 Aug 14 nicklas 132     return lastResults;
6520 18 Aug 14 nicklas 133   }
6520 18 Aug 14 nicklas 134   
6520 18 Aug 14 nicklas 135   /**
5598 30 Mar 11 nicklas 136     Add a file to the ignore list. Files in this list will never
5598 30 Mar 11 nicklas 137     be checked for extensions again. This is only neccessary for XML/JAR
5598 30 Mar 11 nicklas 138     files that doesn't contain extensions.
5598 30 Mar 11 nicklas 139     @param file The file to add
5598 30 Mar 11 nicklas 140     @see #removeIgnore(File)
5598 30 Mar 11 nicklas 141   */
5606 14 Apr 11 nicklas 142   public synchronized void addIgnore(File file)
5598 30 Mar 11 nicklas 143   {
5598 30 Mar 11 nicklas 144     if (file == null) return;
7231 16 Nov 16 nicklas 145     if (ignore.add(file))
7231 16 Nov 16 nicklas 146     {
7231 16 Nov 16 nicklas 147       settings.setIgnoredFile(file, true);
7231 16 Nov 16 nicklas 148       removeFile(file);
7231 16 Nov 16 nicklas 149     }
5598 30 Mar 11 nicklas 150   }
5598 30 Mar 11 nicklas 151   
5598 30 Mar 11 nicklas 152   /**
5598 30 Mar 11 nicklas 153     Remove a file from the ignore list.
5598 30 Mar 11 nicklas 154     @param file The file to remove
5598 30 Mar 11 nicklas 155     @see #addIgnore(File)
5598 30 Mar 11 nicklas 156   */
5606 14 Apr 11 nicklas 157   public synchronized void removeIgnore(File file)
5598 30 Mar 11 nicklas 158   {
5598 30 Mar 11 nicklas 159     if (file == null) return;
7231 16 Nov 16 nicklas 160     if (ignore.remove(file))
7231 16 Nov 16 nicklas 161     {
7231 16 Nov 16 nicklas 162       settings.setIgnoredFile(file, false);
7231 16 Nov 16 nicklas 163     }
5598 30 Mar 11 nicklas 164   }
5598 30 Mar 11 nicklas 165
5603 08 Apr 11 nicklas 166   /**
7231 16 Nov 16 nicklas 167     Get all ignored files. 
7231 16 Nov 16 nicklas 168     
7231 16 Nov 16 nicklas 169     @param onlyInManagedDirectory TRUE to only look for files that
7231 16 Nov 16 nicklas 170       are located in a managed directory
7231 16 Nov 16 nicklas 171     @since 3.10
7231 16 Nov 16 nicklas 172   */
7231 16 Nov 16 nicklas 173   public List<File> getIgnoredFiles(boolean onlyInManagedDirectory)
7231 16 Nov 16 nicklas 174   {
7231 16 Nov 16 nicklas 175     // Create a copy, or the iterator will fail if new
7231 16 Nov 16 nicklas 176     // files are added by another thread
7231 16 Nov 16 nicklas 177     List<File> copy = new ArrayList<File>(ignore.size());
7231 16 Nov 16 nicklas 178     if (onlyInManagedDirectory)
7231 16 Nov 16 nicklas 179     {
7231 16 Nov 16 nicklas 180       for (File f : ignore)
7231 16 Nov 16 nicklas 181       {
7231 16 Nov 16 nicklas 182         if (directories.contains(f.getParentFile())) copy.add(f);
7231 16 Nov 16 nicklas 183       }
7231 16 Nov 16 nicklas 184     }
7231 16 Nov 16 nicklas 185     else
7231 16 Nov 16 nicklas 186     {
7231 16 Nov 16 nicklas 187       copy.addAll(ignore);
7231 16 Nov 16 nicklas 188     }
7231 16 Nov 16 nicklas 189     return copy;
7231 16 Nov 16 nicklas 190   }
7231 16 Nov 16 nicklas 191   
7231 16 Nov 16 nicklas 192   /**
5616 27 Apr 11 nicklas 193     Add a directory to this manager. The directory will be monitored
5617 28 Apr 11 nicklas 194     for extension files in the future. Files in the directory that
5617 28 Apr 11 nicklas 195     are installed as per the settings {@link Settings#isInstalledFile(File)}
5617 28 Apr 11 nicklas 196     are loaded into manager. 
5615 19 Apr 11 nicklas 197     <p>
5617 28 Apr 11 nicklas 198     This method call is ignored if the path is not pointing to an
5617 28 Apr 11 nicklas 199     existing directory.
5603 08 Apr 11 nicklas 200     
5603 08 Apr 11 nicklas 201     @param dir An existing directory
5603 08 Apr 11 nicklas 202     @return The number of files in the directory that was
5617 28 Apr 11 nicklas 203       installed
5603 08 Apr 11 nicklas 204   */
5617 28 Apr 11 nicklas 205   public synchronized int addDirectory(File dir)
5598 30 Mar 11 nicklas 206   {
5598 30 Mar 11 nicklas 207     if (dir == null) throw new NullPointerException("dir");
5617 28 Apr 11 nicklas 208     if (!dir.isDirectory()) return 0;
5603 08 Apr 11 nicklas 209     directories.add(dir);
5617 28 Apr 11 nicklas 210     return scanForInstalledFiles(dir, settings);
5598 30 Mar 11 nicklas 211   }
5598 30 Mar 11 nicklas 212   
5598 30 Mar 11 nicklas 213   
5598 30 Mar 11 nicklas 214   /**
5598 30 Mar 11 nicklas 215      Add a file to the manager that is supposed to contain extensions.
5602 07 Apr 11 nicklas 216      The file either be an XML file containing definitions, or a JAR
5602 07 Apr 11 nicklas 217      file with the definitions in the META-INF/extensions.xml path.
5598 30 Mar 11 nicklas 218      
5602 07 Apr 11 nicklas 219     @param file An extensions file (null is not allowed)
5603 08 Apr 11 nicklas 220     @return The extension file object representing the added file
5602 07 Apr 11 nicklas 221   */
5606 14 Apr 11 nicklas 222   public synchronized ExtensionsFile addFile(File file)
5598 30 Mar 11 nicklas 223   {
5602 07 Apr 11 nicklas 224     if (file == null) throw new NullPointerException("file");
5603 08 Apr 11 nicklas 225     ExtensionsFile xtFile = new ExtensionsFile(this, file);
5603 08 Apr 11 nicklas 226     addExtensionsFile(xtFile);
7233 17 Nov 16 nicklas 227     removeIgnore(file);
5603 08 Apr 11 nicklas 228     return xtFile;
5602 07 Apr 11 nicklas 229   }
5602 07 Apr 11 nicklas 230   
5602 07 Apr 11 nicklas 231   /**
5616 27 Apr 11 nicklas 232     Remove a file from the extensions manager. This method will
5616 27 Apr 11 nicklas 233     remove some information about the file from the manager itself,
5616 27 Apr 11 nicklas 234     but it will not touch registered extensions, plug-ins, extracted
5616 27 Apr 11 nicklas 235     resources, etc. The file itself is not deleted from the disk.
5616 27 Apr 11 nicklas 236     <p>
5616 27 Apr 11 nicklas 237     Note that if the file remains in a managed directory it will
5616 27 Apr 11 nicklas 238     reappear in the manager if {@link #scanForChanges()} is
5616 27 Apr 11 nicklas 239     called.
5616 27 Apr 11 nicklas 240     
5616 27 Apr 11 nicklas 241     @param file The file to remove (if null this method is ignored)
5616 27 Apr 11 nicklas 242   */
7232 17 Nov 16 nicklas 243   ExtensionsFile removeFile(File file)
5616 27 Apr 11 nicklas 244   {
7232 17 Nov 16 nicklas 245     if (file == null) return null;
5616 27 Apr 11 nicklas 246     settings.setUninstalledFile(file);
5616 27 Apr 11 nicklas 247     ExtensionsFile xtFile = xtFiles.remove(file.toURI());
7232 17 Nov 16 nicklas 248     if (xtFile != null)
7232 17 Nov 16 nicklas 249     {
7232 17 Nov 16 nicklas 250       unregisterAllObjects(xtFile);
7232 17 Nov 16 nicklas 251     }
7232 17 Nov 16 nicklas 252     return xtFile;
5616 27 Apr 11 nicklas 253   }
5616 27 Apr 11 nicklas 254   
5616 27 Apr 11 nicklas 255   /**
5602 07 Apr 11 nicklas 256      Add an URI that points to an XML file containing definitions.
5602 07 Apr 11 nicklas 257      This method is mostly useful for system-defined extensions that
5602 07 Apr 11 nicklas 258      are defined in a XML file inside a JAR file.
5602 07 Apr 11 nicklas 259      <p>
5602 07 Apr 11 nicklas 260      
5602 07 Apr 11 nicklas 261      This method can't be used for JAR files and if used for
5602 07 Apr 11 nicklas 262      XML files it doesn't detect changes that are made after 
5602 07 Apr 11 nicklas 263      installation.
5602 07 Apr 11 nicklas 264      
5602 07 Apr 11 nicklas 265     @param uri An URI pointing to an extensions file (null is not allowed)
5603 08 Apr 11 nicklas 266     @return The extension file object representing the added uri
5602 07 Apr 11 nicklas 267   */
5606 14 Apr 11 nicklas 268   public synchronized ExtensionsFile addURI(URI uri)
5602 07 Apr 11 nicklas 269   {
5602 07 Apr 11 nicklas 270     if (uri == null) throw new NullPointerException("uri");
5603 08 Apr 11 nicklas 271     ExtensionsFile xtFile = new ExtensionsFile(this, uri);
5603 08 Apr 11 nicklas 272     addExtensionsFile(xtFile);
5603 08 Apr 11 nicklas 273     return xtFile;
5602 07 Apr 11 nicklas 274   }
5603 08 Apr 11 nicklas 275   
5602 07 Apr 11 nicklas 276   private void addExtensionsFile(ExtensionsFile xtFile)
5602 07 Apr 11 nicklas 277   {
5603 08 Apr 11 nicklas 278     xtFile.validate();
5603 08 Apr 11 nicklas 279     xtFiles.put(xtFile.getURI(), xtFile);
5603 08 Apr 11 nicklas 280     log.info("Added file: " + xtFile);
5603 08 Apr 11 nicklas 281   }
5603 08 Apr 11 nicklas 282   
5603 08 Apr 11 nicklas 283   /**
5616 27 Apr 11 nicklas 284     Scan the managed directories for new, updated and deleted files.
5616 27 Apr 11 nicklas 285     This method is used for gathering statistics only. To do something
5616 27 Apr 11 nicklas 286     with the files the {@link #processFiles(ExtensionsFileProcessor)} 
5616 27 Apr 11 nicklas 287     method must be called.
5616 27 Apr 11 nicklas 288     <p>
5616 27 Apr 11 nicklas 289     Note! The new or modified status of files are NOT reset as a result
5616 27 Apr 11 nicklas 290     of calling this method. The status can only be changed as a result
5616 27 Apr 11 nicklas 291     of some processing.
5616 27 Apr 11 nicklas 292     
5616 27 Apr 11 nicklas 293     @return The number of new + modified + deleted files that was found
5603 08 Apr 11 nicklas 294   */
5616 27 Apr 11 nicklas 295   public synchronized int scanForChanges()
5603 08 Apr 11 nicklas 296   {
5603 08 Apr 11 nicklas 297     numNew = 0;
5603 08 Apr 11 nicklas 298     numModified = 0;
5603 08 Apr 11 nicklas 299     numDeleted = 0;
5603 08 Apr 11 nicklas 300     numUnmodified = 0;
7231 16 Nov 16 nicklas 301     numIgnored = 0;
5603 08 Apr 11 nicklas 302     
5616 27 Apr 11 nicklas 303     // 1. Check and count all known files
5603 08 Apr 11 nicklas 304     Iterator<ExtensionsFile> it = xtFiles.values().iterator();
5603 08 Apr 11 nicklas 305     while (it.hasNext())
5598 30 Mar 11 nicklas 306     {
5603 08 Apr 11 nicklas 307       ExtensionsFile xtFile = it.next();
5603 08 Apr 11 nicklas 308       if (!xtFile.exists()) 
5603 08 Apr 11 nicklas 309       {
5607 15 Apr 11 nicklas 310         log.debug("File '" + xtFile + "' has been deleted.");
5603 08 Apr 11 nicklas 311         numDeleted++;
5603 08 Apr 11 nicklas 312       }
5605 12 Apr 11 nicklas 313       else if (xtFile.isNew())
5605 12 Apr 11 nicklas 314       {
5616 27 Apr 11 nicklas 315         log.debug("File '" + xtFile + "' is new. Validating...");
5616 27 Apr 11 nicklas 316         xtFile.validate();
5616 27 Apr 11 nicklas 317         log.debug("File '" + xtFile + "' is " + (xtFile.isValid() ? "valid" : "invalid"));
5605 12 Apr 11 nicklas 318         numNew++;
5605 12 Apr 11 nicklas 319       }
5603 08 Apr 11 nicklas 320       else if (xtFile.checkModified())
5603 08 Apr 11 nicklas 321       {
5607 15 Apr 11 nicklas 322         log.debug("File '" + xtFile + "' is modified. Validating...");
5603 08 Apr 11 nicklas 323         xtFile.validate();
5607 15 Apr 11 nicklas 324         log.debug("File '" + xtFile + "' is " + (xtFile.isValid() ? "valid" : "invalid"));
5603 08 Apr 11 nicklas 325         numModified++;
5603 08 Apr 11 nicklas 326       }
5603 08 Apr 11 nicklas 327       else
5603 08 Apr 11 nicklas 328       {
5603 08 Apr 11 nicklas 329         numUnmodified++;
5603 08 Apr 11 nicklas 330       }
5598 30 Mar 11 nicklas 331     }
5598 30 Mar 11 nicklas 332     
5603 08 Apr 11 nicklas 333     // Scan the managed directories for new files
5603 08 Apr 11 nicklas 334     for (File dir : directories)
5598 30 Mar 11 nicklas 335     {
5616 27 Apr 11 nicklas 336       numNew += scanForNewFiles(dir);
5598 30 Mar 11 nicklas 337     }
5615 19 Apr 11 nicklas 338     
5616 27 Apr 11 nicklas 339     return numModified + numNew + numDeleted;
5603 08 Apr 11 nicklas 340   }
5603 08 Apr 11 nicklas 341   
5603 08 Apr 11 nicklas 342   /**
5603 08 Apr 11 nicklas 343     Get the number of new files that was found in the last
5691 11 Aug 11 nicklas 344     call to {@link #scanForChanges()}.
5603 08 Apr 11 nicklas 345   */
5603 08 Apr 11 nicklas 346   public int getNumNew()
5603 08 Apr 11 nicklas 347   {
5603 08 Apr 11 nicklas 348     return numNew;
5603 08 Apr 11 nicklas 349   }
5603 08 Apr 11 nicklas 350
5603 08 Apr 11 nicklas 351   /**
5603 08 Apr 11 nicklas 352     Get the number of modified files that was found in the last
5691 11 Aug 11 nicklas 353     call to {@link #scanForChanges()}.
5603 08 Apr 11 nicklas 354   */
5603 08 Apr 11 nicklas 355   public int getNumModified()
5603 08 Apr 11 nicklas 356   {
5603 08 Apr 11 nicklas 357     return numModified;
5603 08 Apr 11 nicklas 358   }
5603 08 Apr 11 nicklas 359
5603 08 Apr 11 nicklas 360   /**
5603 08 Apr 11 nicklas 361     Get the number of unmodified files that was found in the last
5691 11 Aug 11 nicklas 362     call to {@link #scanForChanges()}.
5603 08 Apr 11 nicklas 363   */
5603 08 Apr 11 nicklas 364   public int getNumUnmodified()
5603 08 Apr 11 nicklas 365   {
5603 08 Apr 11 nicklas 366     return numUnmodified;
5603 08 Apr 11 nicklas 367   }
5603 08 Apr 11 nicklas 368   
5603 08 Apr 11 nicklas 369   /**
5603 08 Apr 11 nicklas 370     Get the number of deleted files that was found in the last
5691 11 Aug 11 nicklas 371     call to {@link #scanForChanges()}.
5603 08 Apr 11 nicklas 372   */
5603 08 Apr 11 nicklas 373   public int getNumDeleted()
5603 08 Apr 11 nicklas 374   {
5603 08 Apr 11 nicklas 375     return numDeleted;
5603 08 Apr 11 nicklas 376   }
5603 08 Apr 11 nicklas 377   
5603 08 Apr 11 nicklas 378   /**
7231 16 Nov 16 nicklas 379     Get the number of ignored files that was found in the last
7231 16 Nov 16 nicklas 380     call to {@link #scanForChanges()}.
7231 16 Nov 16 nicklas 381     @since 3.10
7231 16 Nov 16 nicklas 382   */
7231 16 Nov 16 nicklas 383   public int getNumIgnored()
7231 16 Nov 16 nicklas 384   {
7231 16 Nov 16 nicklas 385     return numIgnored;
7231 16 Nov 16 nicklas 386   }
7231 16 Nov 16 nicklas 387   
7231 16 Nov 16 nicklas 388   /**
5603 08 Apr 11 nicklas 389     Scan the given directory and add files that are new.
5615 19 Apr 11 nicklas 390     @param dir The directory to scan
5603 08 Apr 11 nicklas 391     @return The number of new files in the given directory
5603 08 Apr 11 nicklas 392   */
5616 27 Apr 11 nicklas 393   private int scanForNewFiles(File dir)
5603 08 Apr 11 nicklas 394   {
5603 08 Apr 11 nicklas 395     int numNew = 0;
5603 08 Apr 11 nicklas 396     
5603 08 Apr 11 nicklas 397     if (!dir.isDirectory()) 
5603 08 Apr 11 nicklas 398     {
5603 08 Apr 11 nicklas 399       log.warn("File '" + dir + "' is not a directory. Ignoring.");
5603 08 Apr 11 nicklas 400       return numNew;
5603 08 Apr 11 nicklas 401     }
5603 08 Apr 11 nicklas 402
5603 08 Apr 11 nicklas 403     log.info("Scanning directory: " + dir);
5603 08 Apr 11 nicklas 404     FileFilter filter = new RegexpFileFilter(".*\\.(xml|jar)", "");
5603 08 Apr 11 nicklas 405     File[] files = dir.listFiles(filter);
5603 08 Apr 11 nicklas 406     
5603 08 Apr 11 nicklas 407     if (files != null && files.length > 0)
5603 08 Apr 11 nicklas 408     {
5607 15 Apr 11 nicklas 409       log.debug("Found " + files.length + " files");
5603 08 Apr 11 nicklas 410       for (File file : files)
5603 08 Apr 11 nicklas 411       {
5603 08 Apr 11 nicklas 412         if (ignore.contains(file))
5603 08 Apr 11 nicklas 413         {
5607 15 Apr 11 nicklas 414           log.debug("File '" + file + "' is ignored.");
7231 16 Nov 16 nicklas 415           numIgnored++;
5603 08 Apr 11 nicklas 416           continue; // with the next file
5603 08 Apr 11 nicklas 417         }
5603 08 Apr 11 nicklas 418         
5603 08 Apr 11 nicklas 419         URI uri = file.toURI();
5603 08 Apr 11 nicklas 420         ExtensionsFile xtFile = xtFiles.get(uri);
5603 08 Apr 11 nicklas 421         if (xtFile != null)
5603 08 Apr 11 nicklas 422         {
5607 15 Apr 11 nicklas 423           log.debug("File '" + xtFile + "' is a known file.");
5603 08 Apr 11 nicklas 424         }
5603 08 Apr 11 nicklas 425         else
5603 08 Apr 11 nicklas 426         {
5616 27 Apr 11 nicklas 427           log.debug("Adding file: " + file);
5616 27 Apr 11 nicklas 428           xtFile = new ExtensionsFile(this, file);
5616 27 Apr 11 nicklas 429           addExtensionsFile(xtFile);
5616 27 Apr 11 nicklas 430           numNew++;
5603 08 Apr 11 nicklas 431         }
5603 08 Apr 11 nicklas 432       }
5616 27 Apr 11 nicklas 433       log.info("Found " + numNew + " new *.xml and *.jar files");
5603 08 Apr 11 nicklas 434     }
5598 30 Mar 11 nicklas 435     else
5598 30 Mar 11 nicklas 436     {
5616 27 Apr 11 nicklas 437       log.info("Found no *.xml or *.jar files");
5598 30 Mar 11 nicklas 438     }
5603 08 Apr 11 nicklas 439     return numNew;
5598 30 Mar 11 nicklas 440   }
5598 30 Mar 11 nicklas 441   
5602 07 Apr 11 nicklas 442   /**
5617 28 Apr 11 nicklas 443     Scan the given directory and add files that are installed
5617 28 Apr 11 nicklas 444     according to the settings.
5617 28 Apr 11 nicklas 445     @param dir The directory to scan
5617 28 Apr 11 nicklas 446     @return The number of installed files in the given directory
5617 28 Apr 11 nicklas 447   */
5617 28 Apr 11 nicklas 448   private int scanForInstalledFiles(File dir, Settings settings)
5617 28 Apr 11 nicklas 449   {
5617 28 Apr 11 nicklas 450     int numInstalled = 0;
5617 28 Apr 11 nicklas 451     
5617 28 Apr 11 nicklas 452     if (!dir.isDirectory()) 
5617 28 Apr 11 nicklas 453     {
5617 28 Apr 11 nicklas 454       log.warn("File '" + dir + "' is not a directory. Ignoring.");
5617 28 Apr 11 nicklas 455       return numInstalled;
5617 28 Apr 11 nicklas 456     }
5617 28 Apr 11 nicklas 457   
5617 28 Apr 11 nicklas 458     log.info("Scanning directory: " + dir);
5617 28 Apr 11 nicklas 459     FileFilter filter = new RegexpFileFilter(".*\\.(xml|jar)", "");
5617 28 Apr 11 nicklas 460     File[] files = dir.listFiles(filter);
5617 28 Apr 11 nicklas 461     
5617 28 Apr 11 nicklas 462     if (files != null && files.length > 0)
5617 28 Apr 11 nicklas 463     {
5617 28 Apr 11 nicklas 464       log.debug("Found " + files.length + " files");
5617 28 Apr 11 nicklas 465       for (File file : files)
5617 28 Apr 11 nicklas 466       {
5617 28 Apr 11 nicklas 467         if (ignore.contains(file))
5617 28 Apr 11 nicklas 468         {
5617 28 Apr 11 nicklas 469           log.debug("File '" + file + "' is ignored.");
7231 16 Nov 16 nicklas 470           numIgnored++;
5617 28 Apr 11 nicklas 471           continue; // with the next file
5617 28 Apr 11 nicklas 472         }
5617 28 Apr 11 nicklas 473         
5617 28 Apr 11 nicklas 474         URI uri = file.toURI();
5617 28 Apr 11 nicklas 475         ExtensionsFile xtFile = xtFiles.get(uri);
5617 28 Apr 11 nicklas 476         if (xtFile != null)
5617 28 Apr 11 nicklas 477         {
5617 28 Apr 11 nicklas 478           log.debug("File '" + xtFile + "' is a known file.");
5617 28 Apr 11 nicklas 479         }
5617 28 Apr 11 nicklas 480         else if (settings.isInstalledFile(file))
5617 28 Apr 11 nicklas 481         {
5617 28 Apr 11 nicklas 482           log.debug("Adding installed file: " + file);
5617 28 Apr 11 nicklas 483           xtFile = new ExtensionsFile(this, file);
5617 28 Apr 11 nicklas 484           addExtensionsFile(xtFile);
5617 28 Apr 11 nicklas 485           numInstalled++;
5617 28 Apr 11 nicklas 486         }
5617 28 Apr 11 nicklas 487         else
5617 28 Apr 11 nicklas 488         {
5617 28 Apr 11 nicklas 489           log.debug("File '" + file + "' is not installed.");
5617 28 Apr 11 nicklas 490         }
5617 28 Apr 11 nicklas 491       }
5617 28 Apr 11 nicklas 492       log.info("Found " + numInstalled + " new *.xml and *.jar files");
5617 28 Apr 11 nicklas 493     }
5617 28 Apr 11 nicklas 494     else
5617 28 Apr 11 nicklas 495     {
5617 28 Apr 11 nicklas 496       log.info("Found no *.xml or *.jar files");
5617 28 Apr 11 nicklas 497     }
5617 28 Apr 11 nicklas 498     return numInstalled;
5617 28 Apr 11 nicklas 499   }
5617 28 Apr 11 nicklas 500
5617 28 Apr 11 nicklas 501   
5617 28 Apr 11 nicklas 502   /**
5602 07 Apr 11 nicklas 503     Get a list of all extension files managed by this manager.
5602 07 Apr 11 nicklas 504     @return A list
5602 07 Apr 11 nicklas 505   */
5601 01 Apr 11 nicklas 506   public List<ExtensionsFile> getFiles()
5601 01 Apr 11 nicklas 507   {
5601 01 Apr 11 nicklas 508     // Create a copy, or the iterator will fail if new
5601 01 Apr 11 nicklas 509     // files are added by another thread
5602 07 Apr 11 nicklas 510     List<ExtensionsFile> copy = new ArrayList<ExtensionsFile>(xtFiles.values());
5601 01 Apr 11 nicklas 511     return copy;
5601 01 Apr 11 nicklas 512   }
5601 01 Apr 11 nicklas 513
5598 30 Mar 11 nicklas 514   /**
5602 07 Apr 11 nicklas 515     Get the extension file that is located at the given URI. 
5602 07 Apr 11 nicklas 516     This method can be used for all files even those that
5602 07 Apr 11 nicklas 517     have been added with {@link #addFile(File)} or
5602 07 Apr 11 nicklas 518     {@link #addDirectory(File)}.
5601 01 Apr 11 nicklas 519     
5602 07 Apr 11 nicklas 520     @param uri An URI that is pointing to an extensions file
5602 07 Apr 11 nicklas 521      @return An extensions file object, or null if no file is found
5602 07 Apr 11 nicklas 522   */
5602 07 Apr 11 nicklas 523   public ExtensionsFile getFileByURI(String uri)
5602 07 Apr 11 nicklas 524   {
5602 07 Apr 11 nicklas 525     return xtFiles.get(URI.create(uri));
5602 07 Apr 11 nicklas 526   }
5602 07 Apr 11 nicklas 527   
5602 07 Apr 11 nicklas 528   /**
5602 07 Apr 11 nicklas 529     Find out which file the object with the given key was defined.
5602 07 Apr 11 nicklas 530     See implementations of {@link ObjectKey} for various types
5602 07 Apr 11 nicklas 531     of objects to look for.
5601 01 Apr 11 nicklas 532     
5602 07 Apr 11 nicklas 533     @param key The object key
5601 01 Apr 11 nicklas 534     @return Information about the file the object is defined in,
5602 07 Apr 11 nicklas 535       or null if no object is found for the given key
5601 01 Apr 11 nicklas 536   */
5606 14 Apr 11 nicklas 537   public ExtensionsFile getFileByObjectKey(ObjectKey<?> key)
5601 01 Apr 11 nicklas 538   {
5602 07 Apr 11 nicklas 539     return installedObjects.get(key);
5601 01 Apr 11 nicklas 540   }
5601 01 Apr 11 nicklas 541   
5602 07 Apr 11 nicklas 542   /**
5602 07 Apr 11 nicklas 543     Register an object as defined by the given extensions file.
5602 07 Apr 11 nicklas 544     
5602 07 Apr 11 nicklas 545     @param key The object key
5606 14 Apr 11 nicklas 546     @param xtFile The extensions file the object is defined in
5602 07 Apr 11 nicklas 547   */
5606 14 Apr 11 nicklas 548   void registerObject(ObjectKey<?> key, ExtensionsFile xtFile)
5601 01 Apr 11 nicklas 549   {
5606 14 Apr 11 nicklas 550     ExtensionsFile xtOther = installedObjects.get(key);
5606 14 Apr 11 nicklas 551     if (xtOther == null)
5606 14 Apr 11 nicklas 552     {
5606 14 Apr 11 nicklas 553       installedObjects.put(key, xtFile);
5606 14 Apr 11 nicklas 554     }
5607 15 Apr 11 nicklas 555     else if (!xtFile.equals(xtOther))
5606 14 Apr 11 nicklas 556     {
5606 14 Apr 11 nicklas 557       throw new ItemAlreadyExistsException(key.toDescription() + " is defined in '" + xtOther.getName() + "'");
5606 14 Apr 11 nicklas 558     }
5601 01 Apr 11 nicklas 559   }
5601 01 Apr 11 nicklas 560   
5601 01 Apr 11 nicklas 561   /**
5606 14 Apr 11 nicklas 562     Unregister an object. The given extensions file must match
5606 14 Apr 11 nicklas 563     be equal to the one that registered the object, otherwise
5606 14 Apr 11 nicklas 564     the unregistration is ignored.
5606 14 Apr 11 nicklas 565     
5603 08 Apr 11 nicklas 566     @param key The object key
5606 14 Apr 11 nicklas 567     @param xtFile The extensions file the object is defined in
5603 08 Apr 11 nicklas 568   */
5606 14 Apr 11 nicklas 569   void unregisterObject(ObjectKey<?> key, ExtensionsFile xtFile)
5603 08 Apr 11 nicklas 570   {
5606 14 Apr 11 nicklas 571     if (xtFile.equals(installedObjects.get(key)))
5606 14 Apr 11 nicklas 572     {
5606 14 Apr 11 nicklas 573       installedObjects.remove(key);
5606 14 Apr 11 nicklas 574     }
5603 08 Apr 11 nicklas 575   }
5603 08 Apr 11 nicklas 576   
5605 12 Apr 11 nicklas 577   /**
5605 12 Apr 11 nicklas 578     Unregister all objects know in the file.
5605 12 Apr 11 nicklas 579   */
5605 12 Apr 11 nicklas 580   void unregisterAllObjects(ExtensionsFile xtFile)
5605 12 Apr 11 nicklas 581   {
5605 12 Apr 11 nicklas 582     Iterator<ExtensionsFile> it = installedObjects.values().iterator();
5605 12 Apr 11 nicklas 583     while (it.hasNext())
5605 12 Apr 11 nicklas 584     {
5605 12 Apr 11 nicklas 585       if (it.next().equals(xtFile)) it.remove();
5605 12 Apr 11 nicklas 586     }
5605 12 Apr 11 nicklas 587   }
5605 12 Apr 11 nicklas 588   
5606 14 Apr 11 nicklas 589   /**
5606 14 Apr 11 nicklas 590     Process all know file with the given processer.
5606 14 Apr 11 nicklas 591     @param processor A processor implementation, null is not allowed
5606 14 Apr 11 nicklas 592   */
5603 08 Apr 11 nicklas 593   public void processFiles(ExtensionsFileProcessor processor)
5603 08 Apr 11 nicklas 594   {
5603 08 Apr 11 nicklas 595     processFiles(processor, null);
5603 08 Apr 11 nicklas 596   }
5603 08 Apr 11 nicklas 597   
5603 08 Apr 11 nicklas 598   /**
5598 30 Mar 11 nicklas 599     Process all known extension files with the given processor.
5603 08 Apr 11 nicklas 600     
5598 30 Mar 11 nicklas 601     @param processor A processor implementation, null is not allowed
5603 08 Apr 11 nicklas 602     @param filter An optional filter implementation that can be used to
5603 08 Apr 11 nicklas 603       limit which files are sent to the processor
5598 30 Mar 11 nicklas 604   */
5606 14 Apr 11 nicklas 605   public synchronized void processFiles(ExtensionsFileProcessor processor, Filter<ExtensionsFile> filter)
5598 30 Mar 11 nicklas 606   {
5598 30 Mar 11 nicklas 607     if (processor == null) throw new NullPointerException("processor");
5607 15 Apr 11 nicklas 608     log.info("Processing files with processor: " + processor);
5603 08 Apr 11 nicklas 609
5603 08 Apr 11 nicklas 610     // 1. Filter files with the given filter
5603 08 Apr 11 nicklas 611     Collection<ExtensionsFile> filtered = null;
5603 08 Apr 11 nicklas 612     if (filter != null)
5598 30 Mar 11 nicklas 613     {
5603 08 Apr 11 nicklas 614       filtered = new ArrayList<ExtensionsFile>(xtFiles.size());
5607 15 Apr 11 nicklas 615       log.info("Filtering files with filter: " + filter);
5602 07 Apr 11 nicklas 616       for (ExtensionsFile xtFile : xtFiles.values())
5598 30 Mar 11 nicklas 617       {
5603 08 Apr 11 nicklas 618         if (filter.evaluate(xtFile)) 
5603 08 Apr 11 nicklas 619         {
5603 08 Apr 11 nicklas 620           filtered.add(xtFile);
5603 08 Apr 11 nicklas 621         }
5603 08 Apr 11 nicklas 622         else
5603 08 Apr 11 nicklas 623         {
5603 08 Apr 11 nicklas 624           log.debug("File blocked by filter: " + xtFile);
5603 08 Apr 11 nicklas 625         }
5603 08 Apr 11 nicklas 626       }
5603 08 Apr 11 nicklas 627     }
5603 08 Apr 11 nicklas 628     else
5603 08 Apr 11 nicklas 629     {
5603 08 Apr 11 nicklas 630       filtered = xtFiles.values();
5603 08 Apr 11 nicklas 631     }
5603 08 Apr 11 nicklas 632     
5603 08 Apr 11 nicklas 633     // 2. Process the remaining files
5603 08 Apr 11 nicklas 634     int numFiles = filtered.size();
5603 08 Apr 11 nicklas 635     List<WriteableExtensionsFile> writeableFiles = new ArrayList<WriteableExtensionsFile>(numFiles);
5603 08 Apr 11 nicklas 636     try
5603 08 Apr 11 nicklas 637     {
5603 08 Apr 11 nicklas 638       log.debug("Processing " + numFiles + " file(s) with processor: " + processor);
5603 08 Apr 11 nicklas 639       processor.begin(this, numFiles);
5603 08 Apr 11 nicklas 640       for (ExtensionsFile xtFile : filtered)
5603 08 Apr 11 nicklas 641       {
5598 30 Mar 11 nicklas 642         log.debug("Processing file: " + xtFile);
5602 07 Apr 11 nicklas 643         WriteableExtensionsFile wf = xtFile.getWriteableFile();
5602 07 Apr 11 nicklas 644         writeableFiles.add(wf);
5602 07 Apr 11 nicklas 645         processor.processFile(this, wf);
5598 30 Mar 11 nicklas 646       }
5598 30 Mar 11 nicklas 647       processor.done(this);
5607 15 Apr 11 nicklas 648       log.debug("All files processed with processor: " + processor);
5598 30 Mar 11 nicklas 649     }
5598 30 Mar 11 nicklas 650     catch (Throwable t)
5598 30 Mar 11 nicklas 651     {
5607 15 Apr 11 nicklas 652       log.error("Error when processing extensions with " + processor, t);
5598 30 Mar 11 nicklas 653       processor.done(null, t);
5598 30 Mar 11 nicklas 654     }
5602 07 Apr 11 nicklas 655     finally
5602 07 Apr 11 nicklas 656     {
5602 07 Apr 11 nicklas 657       // Make sure all WriteableExtensionFile:s are closed if the processor "forgets" it
5602 07 Apr 11 nicklas 658       for (WriteableExtensionsFile wf : writeableFiles)
5602 07 Apr 11 nicklas 659       {
5602 07 Apr 11 nicklas 660         wf.close();
5602 07 Apr 11 nicklas 661       }
5602 07 Apr 11 nicklas 662     }
5598 30 Mar 11 nicklas 663   }
5598 30 Mar 11 nicklas 664   
5606 14 Apr 11 nicklas 665   /**
5606 14 Apr 11 nicklas 666     Compare URI:s so that 'jar:' scheme always are listed first. This means
5606 14 Apr 11 nicklas 667     that the core defintion files are always displayed before any external
5606 14 Apr 11 nicklas 668     extensions.
5606 14 Apr 11 nicklas 669   */
5605 12 Apr 11 nicklas 670   public static class JarFirstURIComparator
5605 12 Apr 11 nicklas 671     implements Comparator<URI>
5605 12 Apr 11 nicklas 672   {
5605 12 Apr 11 nicklas 673     public JarFirstURIComparator()
5605 12 Apr 11 nicklas 674     {}
5605 12 Apr 11 nicklas 675
5605 12 Apr 11 nicklas 676     @Override
5605 12 Apr 11 nicklas 677     public int compare(URI u1, URI u2) 
5605 12 Apr 11 nicklas 678     {
5605 12 Apr 11 nicklas 679       String s1 = u1.getScheme();
5605 12 Apr 11 nicklas 680       String s2 = u2.getScheme();
5605 12 Apr 11 nicklas 681       if (!s1.equals(s2))
5605 12 Apr 11 nicklas 682       {
5605 12 Apr 11 nicklas 683         if (s1.equals("jar")) return -1;
5605 12 Apr 11 nicklas 684         if (s2.equals("jar")) return 1;
5605 12 Apr 11 nicklas 685       }
5605 12 Apr 11 nicklas 686       return u1.compareTo(u2);
5605 12 Apr 11 nicklas 687     }
5605 12 Apr 11 nicklas 688   }
5605 12 Apr 11 nicklas 689   
5598 30 Mar 11 nicklas 690 }