src/core/net/sf/basedb/util/extensions/manager/ExtensionsFile.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.FileNotFoundException;
5598 30 Mar 11 nicklas 26 import java.io.IOException;
5598 30 Mar 11 nicklas 27 import java.io.InputStream;
5598 30 Mar 11 nicklas 28 import java.net.URI;
5602 07 Apr 11 nicklas 29 import java.util.ArrayList;
5601 01 Apr 11 nicklas 30 import java.util.HashMap;
5602 07 Apr 11 nicklas 31 import java.util.LinkedHashMap;
5602 07 Apr 11 nicklas 32 import java.util.List;
5601 01 Apr 11 nicklas 33 import java.util.Map;
5602 07 Apr 11 nicklas 34 import java.util.concurrent.TimeUnit;
5602 07 Apr 11 nicklas 35 import java.util.concurrent.locks.ReentrantReadWriteLock;
5598 30 Mar 11 nicklas 36 import java.util.zip.ZipEntry;
5598 30 Mar 11 nicklas 37 import java.util.zip.ZipFile;
5598 30 Mar 11 nicklas 38
5598 30 Mar 11 nicklas 39 import net.sf.basedb.core.plugin.About;
5598 30 Mar 11 nicklas 40 import net.sf.basedb.util.FileUtil;
5598 30 Mar 11 nicklas 41 import net.sf.basedb.util.JarClassLoader;
5602 07 Apr 11 nicklas 42 import net.sf.basedb.util.extensions.Extension;
5602 07 Apr 11 nicklas 43 import net.sf.basedb.util.extensions.ExtensionPoint;
5598 30 Mar 11 nicklas 44 import net.sf.basedb.util.extensions.xml.XmlLoader;
5598 30 Mar 11 nicklas 45 import net.sf.basedb.util.uri.CloseResourceInputStream;
5598 30 Mar 11 nicklas 46
5598 30 Mar 11 nicklas 47 /**
5598 30 Mar 11 nicklas 48   Represents a file with extensions in it. The file is either an XML file
5598 30 Mar 11 nicklas 49   or a JAR file. A JAR file is always a real file on the file system and
5598 30 Mar 11 nicklas 50   can be accessed with {@link #getFile()}. An XML file can be both a real file
5598 30 Mar 11 nicklas 51   or an abstract stream of bytes and can be accessed by {@link #getXmlStream()}.
5598 30 Mar 11 nicklas 52   The latter method can also be used in the case of a JAR file and will then
5598 30 Mar 11 nicklas 53   open the <code>META-INF/extensions.xml</code> file.
5598 30 Mar 11 nicklas 54
5598 30 Mar 11 nicklas 55
5598 30 Mar 11 nicklas 56   @author Nicklas
5598 30 Mar 11 nicklas 57   @since 3.0
5598 30 Mar 11 nicklas 58   @base.modified $Date$
5598 30 Mar 11 nicklas 59  */
5598 30 Mar 11 nicklas 60 public class ExtensionsFile
5600 01 Apr 11 nicklas 61   implements Comparable<ExtensionsFile>
5598 30 Mar 11 nicklas 62 {
6444 09 Apr 14 nicklas 63   private static final org.slf4j.Logger log = 
6444 09 Apr 14 nicklas 64     org.slf4j.LoggerFactory.getLogger(ExtensionsFile.class);
5598 30 Mar 11 nicklas 65
5602 07 Apr 11 nicklas 66   private final ExtensionsManager manager;
5598 30 Mar 11 nicklas 67   private final File file;
5598 30 Mar 11 nicklas 68   private final URI uri;
5598 30 Mar 11 nicklas 69   private final boolean isJar;
5598 30 Mar 11 nicklas 70   private final String name;
5603 08 Apr 11 nicklas 71   private final ReentrantReadWriteLock rwLock;
6875 20 Apr 15 nicklas 72   private final Map<ObjectKey<?>, Object> allObjects;
6875 20 Apr 15 nicklas 73   private final Map<ObjectKey<?>, Object> objectMetadata;
5598 30 Mar 11 nicklas 74   
5603 08 Apr 11 nicklas 75   private boolean isNew;
5603 08 Apr 11 nicklas 76   private boolean wasModified;
5603 08 Apr 11 nicklas 77   private long lastModified;
5603 08 Apr 11 nicklas 78   private long lastLength;
5598 30 Mar 11 nicklas 79   private JarClassLoader jarLoader;
5598 30 Mar 11 nicklas 80   private About about;
5602 07 Apr 11 nicklas 81   private boolean hasError;
5598 30 Mar 11 nicklas 82
5598 30 Mar 11 nicklas 83   private volatile boolean isValid;
5598 30 Mar 11 nicklas 84   private Throwable validationError;
5598 30 Mar 11 nicklas 85
5598 30 Mar 11 nicklas 86   /**
5598 30 Mar 11 nicklas 87     Create a new extensions file backed by a real file on the
5598 30 Mar 11 nicklas 88     file system. If the file name ends with '.jar' it is assumed
5598 30 Mar 11 nicklas 89     to be a JAR file, otherwise it must be an XML file containing
5598 30 Mar 11 nicklas 90     extension definitions.
5598 30 Mar 11 nicklas 91     @param file The file
5598 30 Mar 11 nicklas 92   */
5602 07 Apr 11 nicklas 93   ExtensionsFile(ExtensionsManager manager, File file)
5598 30 Mar 11 nicklas 94   {
5602 07 Apr 11 nicklas 95     this(manager, file, file.getName().endsWith(".jar"));
5598 30 Mar 11 nicklas 96   }
5598 30 Mar 11 nicklas 97   
5598 30 Mar 11 nicklas 98   /**
5598 30 Mar 11 nicklas 99     Create a new extensions file backed by a real file
5598 30 Mar 11 nicklas 100     on the file system. The file may be a JAR file or an
5598 30 Mar 11 nicklas 101     XML file.
5598 30 Mar 11 nicklas 102     
5598 30 Mar 11 nicklas 103     @param file The file
5598 30 Mar 11 nicklas 104     @param isJar TRUE if it is a JAR file, FALSE if it is an XML file
5598 30 Mar 11 nicklas 105    */
5602 07 Apr 11 nicklas 106   ExtensionsFile(ExtensionsManager manager, File file, boolean isJar)
5598 30 Mar 11 nicklas 107   {
5602 07 Apr 11 nicklas 108     this(manager, file, file.toURI(), file.getName(), isJar);
5598 30 Mar 11 nicklas 109   }
5598 30 Mar 11 nicklas 110
5598 30 Mar 11 nicklas 111   /**
5598 30 Mar 11 nicklas 112     Create a new extension file backed by an URI. The URI must point to
5598 30 Mar 11 nicklas 113     an XML file containing extension definitions.
5598 30 Mar 11 nicklas 114     @param uri The URI
5598 30 Mar 11 nicklas 115   */
5602 07 Apr 11 nicklas 116   ExtensionsFile(ExtensionsManager manager, URI uri)
5598 30 Mar 11 nicklas 117   {
5602 07 Apr 11 nicklas 118     this(manager, null, uri, uri.toString().replaceFirst(".*/", ""), false);
5602 07 Apr 11 nicklas 119   }
5602 07 Apr 11 nicklas 120   
5602 07 Apr 11 nicklas 121   private ExtensionsFile(ExtensionsManager manager, File file, URI uri, String name, boolean isJar)
5602 07 Apr 11 nicklas 122   {
5602 07 Apr 11 nicklas 123     this.manager = manager;
5602 07 Apr 11 nicklas 124     this.file = file;
5598 30 Mar 11 nicklas 125     this.uri = uri;
5602 07 Apr 11 nicklas 126     this.name = name;
5602 07 Apr 11 nicklas 127     this.isJar = isJar;
5603 08 Apr 11 nicklas 128     this.isNew = true;
5603 08 Apr 11 nicklas 129     this.wasModified = true;
5602 07 Apr 11 nicklas 130     this.rwLock = new ReentrantReadWriteLock();
6875 20 Apr 15 nicklas 131     this.allObjects = new LinkedHashMap<ObjectKey<?>, Object>();
6875 20 Apr 15 nicklas 132     this.objectMetadata = new HashMap<ObjectKey<?>, Object>();
5598 30 Mar 11 nicklas 133   }
5598 30 Mar 11 nicklas 134   
5600 01 Apr 11 nicklas 135   /*
5600 01 Apr 11 nicklas 136     From the Comparable interface
5600 01 Apr 11 nicklas 137     -----------------------------
5600 01 Apr 11 nicklas 138   */
5600 01 Apr 11 nicklas 139   @Override
5600 01 Apr 11 nicklas 140   public int compareTo(ExtensionsFile other)
5600 01 Apr 11 nicklas 141   {
5600 01 Apr 11 nicklas 142     // Compare by name and then by URI
5600 01 Apr 11 nicklas 143     int n = this.name.compareTo(other.name);
5600 01 Apr 11 nicklas 144     return n == 0 ? this.uri.compareTo(other.uri) : n;
5600 01 Apr 11 nicklas 145   }
5600 01 Apr 11 nicklas 146   // ------------------------------
5600 01 Apr 11 nicklas 147   /*
5600 01 Apr 11 nicklas 148     From the Object class
5600 01 Apr 11 nicklas 149     ---------------------
5600 01 Apr 11 nicklas 150   */
5600 01 Apr 11 nicklas 151   @Override
5600 01 Apr 11 nicklas 152   public int hashCode()
5600 01 Apr 11 nicklas 153   {
5600 01 Apr 11 nicklas 154     return uri.hashCode();
5600 01 Apr 11 nicklas 155   }
5600 01 Apr 11 nicklas 156   @Override
5600 01 Apr 11 nicklas 157   public boolean equals(Object o)
5600 01 Apr 11 nicklas 158   {
5600 01 Apr 11 nicklas 159     if (this == o) return true;
5600 01 Apr 11 nicklas 160     if (o == null || this.getClass() != o.getClass()) return false;
5600 01 Apr 11 nicklas 161     ExtensionsFile other = (ExtensionsFile)o;
5600 01 Apr 11 nicklas 162     return this.uri.equals(other.uri);
5600 01 Apr 11 nicklas 163   }
5600 01 Apr 11 nicklas 164   @Override
5600 01 Apr 11 nicklas 165   public String toString()
5600 01 Apr 11 nicklas 166   {
5600 01 Apr 11 nicklas 167     return "ExtensionsFile@" + System.identityHashCode(this) + "[" + uri + "]";
5600 01 Apr 11 nicklas 168   }
5600 01 Apr 11 nicklas 169   // ------------------------------
5600 01 Apr 11 nicklas 170   
5598 30 Mar 11 nicklas 171   /**
5598 30 Mar 11 nicklas 172     Get the name of the file. The name may or may not
5598 30 Mar 11 nicklas 173     correspond to a real file.
5598 30 Mar 11 nicklas 174   */
5598 30 Mar 11 nicklas 175   public String getName()
5598 30 Mar 11 nicklas 176   {
5598 30 Mar 11 nicklas 177     return name;
5598 30 Mar 11 nicklas 178   }
5598 30 Mar 11 nicklas 179   
5598 30 Mar 11 nicklas 180   /**
5598 30 Mar 11 nicklas 181     Get the underlying file on the file system. Return null
5598 30 Mar 11 nicklas 182     if the extension is defined by an URI.
5598 30 Mar 11 nicklas 183   */
5598 30 Mar 11 nicklas 184   public File getFile()
5598 30 Mar 11 nicklas 185   {
5598 30 Mar 11 nicklas 186     return file;
5598 30 Mar 11 nicklas 187   }
5598 30 Mar 11 nicklas 188   
5598 30 Mar 11 nicklas 189   /**
5598 30 Mar 11 nicklas 190     Get an URI that points to the extensions file.
5598 30 Mar 11 nicklas 191   */
5598 30 Mar 11 nicklas 192   public URI getURI()
5598 30 Mar 11 nicklas 193   {
5598 30 Mar 11 nicklas 194     return uri;
5598 30 Mar 11 nicklas 195   }
5598 30 Mar 11 nicklas 196   
5598 30 Mar 11 nicklas 197   /**
5598 30 Mar 11 nicklas 198     If the file name ends with '.jar' the file is considered a JAR file.
5598 30 Mar 11 nicklas 199      Otherwise it is considered an XML file. The file name check is case
5598 30 Mar 11 nicklas 200      sensitive.
5598 30 Mar 11 nicklas 201     @return TRUE if the file is a JAR file, false if it is an XML file
5598 30 Mar 11 nicklas 202   */
5598 30 Mar 11 nicklas 203   public boolean isJar()
5598 30 Mar 11 nicklas 204   {
5598 30 Mar 11 nicklas 205     return isJar;
5598 30 Mar 11 nicklas 206   }
5598 30 Mar 11 nicklas 207   
5598 30 Mar 11 nicklas 208   /**
5603 08 Apr 11 nicklas 209     Check if the file exists. For files that are only represented by
5603 08 Apr 11 nicklas 210     an URI this method always return true. For other files the call 
5823 24 Oct 11 nicklas 211     is forwarded to {@link java.io.File#exists()} and {@link java.io.File#isFile()}.
5823 24 Oct 11 nicklas 212     @return See {@link java.io.File#exists()}
5598 30 Mar 11 nicklas 213   */
5598 30 Mar 11 nicklas 214   public boolean exists()
5598 30 Mar 11 nicklas 215   {
5603 08 Apr 11 nicklas 216     return file == null || (file.exists() && file.isFile());
5598 30 Mar 11 nicklas 217   }
5598 30 Mar 11 nicklas 218
5598 30 Mar 11 nicklas 219   /**
5603 08 Apr 11 nicklas 220     If the file is a new file not previously processed by the
5603 08 Apr 11 nicklas 221     extension system. Thew 'new' status is changed when 
5603 08 Apr 11 nicklas 222     {@link WriteableExtensionsFile#markAsProcessed()} is called which 
5603 08 Apr 11 nicklas 223     usually happens when all extensions has been registered.
5603 08 Apr 11 nicklas 224     @return TRUE if the file is a new file, FALSE otherwise
5598 30 Mar 11 nicklas 225   */
5603 08 Apr 11 nicklas 226   public boolean isNew()
5598 30 Mar 11 nicklas 227   {
5603 08 Apr 11 nicklas 228     return isNew;
5598 30 Mar 11 nicklas 229   }
5603 08 Apr 11 nicklas 230
5603 08 Apr 11 nicklas 231   /**
5616 27 Apr 11 nicklas 232     Is this file installed into the system or not?
5616 27 Apr 11 nicklas 233   */
5616 27 Apr 11 nicklas 234   public boolean isInstalled()
5616 27 Apr 11 nicklas 235   {
5616 27 Apr 11 nicklas 236     return file == null || manager.getSettings().isInstalledFile(file);
5616 27 Apr 11 nicklas 237   }
5616 27 Apr 11 nicklas 238   
5616 27 Apr 11 nicklas 239   /**
7232 17 Nov 16 nicklas 240     Is this an extension file that should be ignored?
7232 17 Nov 16 nicklas 241     @since 3.10
7232 17 Nov 16 nicklas 242   */
7232 17 Nov 16 nicklas 243   public boolean isIgnored()
7232 17 Nov 16 nicklas 244   {
7232 17 Nov 16 nicklas 245     return file != null && manager.getSettings().isIgnoredFile(file);
7232 17 Nov 16 nicklas 246   }
7232 17 Nov 16 nicklas 247
7232 17 Nov 16 nicklas 248   /**
5603 08 Apr 11 nicklas 249     Check if the file was modified when the last call to
5603 08 Apr 11 nicklas 250     {@link #checkModified()} was made. It is recommended
5603 08 Apr 11 nicklas 251     that processor implementation use this method instead
5603 08 Apr 11 nicklas 252     to avoid inconsistent behaviour if the file happens to
5603 08 Apr 11 nicklas 253     be modified while a processor is running.
5603 08 Apr 11 nicklas 254     
5603 08 Apr 11 nicklas 255     @return TRUE if the file was modified, FALSE otherwise
5603 08 Apr 11 nicklas 256   */
5603 08 Apr 11 nicklas 257   public boolean wasModified()
5603 08 Apr 11 nicklas 258   {
5603 08 Apr 11 nicklas 259     return wasModified;
5603 08 Apr 11 nicklas 260   }
5598 30 Mar 11 nicklas 261   
5598 30 Mar 11 nicklas 262   /**
5603 08 Apr 11 nicklas 263     Check if the underlying file has been modified since it was
5603 08 Apr 11 nicklas 264     last processed. Files that are new are always considered to be
5603 08 Apr 11 nicklas 265     modified. Files that are only represented as an URI are considered
5603 08 Apr 11 nicklas 266     to be unmodifiable. For other files, we check the 
5823 24 Oct 11 nicklas 267     {@link java.io.File#lastModified()} and {@link java.io.File#length()} of the 
5603 08 Apr 11 nicklas 268     underlying file and compare that to the last known timestamp and size. 
5603 08 Apr 11 nicklas 269     Files that are JAR files we also check with the class loader if any of
5603 08 Apr 11 nicklas 270     the libraries it uses have changed.
5603 08 Apr 11 nicklas 271     <p>
5603 08 Apr 11 nicklas 272     The result of this call is remembered until this method is called 
5603 08 Apr 11 nicklas 273     again or until the file is marked as processed.
5603 08 Apr 11 nicklas 274     
5603 08 Apr 11 nicklas 275     @see WriteableExtensionsFile#markAsProcessed()
5603 08 Apr 11 nicklas 276     @see #wasModified()
5598 30 Mar 11 nicklas 277   */
5603 08 Apr 11 nicklas 278   public boolean checkModified()
5598 30 Mar 11 nicklas 279   {
7226 15 Nov 16 nicklas 280     log.debug("Checking if file is modified: " + uri);
5603 08 Apr 11 nicklas 281     wasModified = false; // Assume not modified as a starting point
5603 08 Apr 11 nicklas 282     if (isNew)
5603 08 Apr 11 nicklas 283     {
5603 08 Apr 11 nicklas 284       // New files are always modified
7226 15 Nov 16 nicklas 285       log.debug("File was new: " + uri);
5603 08 Apr 11 nicklas 286       wasModified = true;
5603 08 Apr 11 nicklas 287     }
5603 08 Apr 11 nicklas 288     else if (file != null && (lastModified != file.lastModified() || lastLength != file.length()))
5603 08 Apr 11 nicklas 289     {
5603 08 Apr 11 nicklas 290       // The XML or JAR file has changed
7226 15 Nov 16 nicklas 291       log.debug("File has been modified: " + uri);
5603 08 Apr 11 nicklas 292       wasModified = true;
5603 08 Apr 11 nicklas 293     }
5603 08 Apr 11 nicklas 294     else if (isJar && jarLoader != null && jarLoader.hasChanged(true))
5603 08 Apr 11 nicklas 295     {
5603 08 Apr 11 nicklas 296       // JAR files have changed if the class loader detect changes
7226 15 Nov 16 nicklas 297       log.debug("Class path has been modified: " + uri);
5603 08 Apr 11 nicklas 298       wasModified = true;
5603 08 Apr 11 nicklas 299     }
7226 15 Nov 16 nicklas 300     else
7226 15 Nov 16 nicklas 301     {
7226 15 Nov 16 nicklas 302       log.debug("File has not been modified: " + uri);
7226 15 Nov 16 nicklas 303     }
5603 08 Apr 11 nicklas 304     return wasModified;
5598 30 Mar 11 nicklas 305   }
5603 08 Apr 11 nicklas 306   
5603 08 Apr 11 nicklas 307   /**
5603 08 Apr 11 nicklas 308     Is the file a valid extensions file according to the last
5603 08 Apr 11 nicklas 309     performed validation. 
5603 08 Apr 11 nicklas 310     
5603 08 Apr 11 nicklas 311     This means that the file must be either:
5603 08 Apr 11 nicklas 312     <ul>
5603 08 Apr 11 nicklas 313     <li>An extensions definition XML file 
5603 08 Apr 11 nicklas 314     <li>A JAR file with an extensions definition XML file at
5603 08 Apr 11 nicklas 315       <code>META-INF/extensions.xml</code>.
5603 08 Apr 11 nicklas 316     </ul>
5603 08 Apr 11 nicklas 317     
5603 08 Apr 11 nicklas 318     The result of the validation is remembered as long as the 
5603 08 Apr 11 nicklas 319     file is not modified. If {@link #checkModified()} returns
5603 08 Apr 11 nicklas 320     true, the return value of this method may be out of sync.
5603 08 Apr 11 nicklas 321     Re-validation of modified files are usually performed
5603 08 Apr 11 nicklas 322     by the extensions manager by calling
5691 11 Aug 11 nicklas 323     {@link ExtensionsManager#scanForChanges()}
5603 08 Apr 11 nicklas 324     
5603 08 Apr 11 nicklas 325     <p>
5603 08 Apr 11 nicklas 326     NOTE! The validation will only verify that the XML file 
5603 08 Apr 11 nicklas 327     follows the rules defined by the schema definition. It will
5603 08 Apr 11 nicklas 328     not check that actual classes, etc. exists and can be created.
5603 08 Apr 11 nicklas 329     This usually happens later in the registration process, and
5603 08 Apr 11 nicklas 330     can be checked by {@link #hasError()}.
5603 08 Apr 11 nicklas 331     
5603 08 Apr 11 nicklas 332     @return TRUE if the file is valid, FALSE otherwise
5603 08 Apr 11 nicklas 333     @see #getValidationError()
5603 08 Apr 11 nicklas 334     @see #checkModified()
5603 08 Apr 11 nicklas 335     @see #hasError()
5603 08 Apr 11 nicklas 336   */
5603 08 Apr 11 nicklas 337   public boolean isValid()
5603 08 Apr 11 nicklas 338   {
5603 08 Apr 11 nicklas 339     return isValid;
5603 08 Apr 11 nicklas 340   }
5598 30 Mar 11 nicklas 341
5603 08 Apr 11 nicklas 342   /**
5603 08 Apr 11 nicklas 343     Get more information about the error that caused the validation
5603 08 Apr 11 nicklas 344     to fail.
5603 08 Apr 11 nicklas 345     @return An exception or null if the validation succeeded
5603 08 Apr 11 nicklas 346     @see #isValid()
5603 08 Apr 11 nicklas 347   */
5603 08 Apr 11 nicklas 348   public Throwable getValidationError()
5603 08 Apr 11 nicklas 349   {
5603 08 Apr 11 nicklas 350     return validationError;
5603 08 Apr 11 nicklas 351   }
5603 08 Apr 11 nicklas 352   
5603 08 Apr 11 nicklas 353   /**
5603 08 Apr 11 nicklas 354     Get information about the extensions in this file.
5603 08 Apr 11 nicklas 355     @return An About object or null if the file is not a valid
5603 08 Apr 11 nicklas 356       extensions file
5603 08 Apr 11 nicklas 357   */
5603 08 Apr 11 nicklas 358   public About getAbout()
5603 08 Apr 11 nicklas 359   {
5603 08 Apr 11 nicklas 360     return about;
5603 08 Apr 11 nicklas 361   }
5603 08 Apr 11 nicklas 362
5603 08 Apr 11 nicklas 363   /**
5603 08 Apr 11 nicklas 364     Get an input stream for reading the XML file containing the extension
5603 08 Apr 11 nicklas 365     definitions.
5603 08 Apr 11 nicklas 366     @return An input stream
5603 08 Apr 11 nicklas 367   */
5598 30 Mar 11 nicklas 368   public InputStream getXmlStream()
5598 30 Mar 11 nicklas 369     throws IOException
5598 30 Mar 11 nicklas 370   {
5598 30 Mar 11 nicklas 371     InputStream in = null;
5598 30 Mar 11 nicklas 372     if (isJar())
5598 30 Mar 11 nicklas 373     {
6880 21 Apr 15 nicklas 374       ZipFile zipFile = null;
6880 21 Apr 15 nicklas 375       try
5598 30 Mar 11 nicklas 376       {
6880 21 Apr 15 nicklas 377         zipFile = new ZipFile(getFile());
6880 21 Apr 15 nicklas 378         ZipEntry zipEntry = zipFile.getEntry("META-INF/extensions.xml");
6880 21 Apr 15 nicklas 379         if (zipEntry == null)
6880 21 Apr 15 nicklas 380         {
7233 17 Nov 16 nicklas 381           throw new FileNotFoundException(name+"!META-INF/extensions.xml");
6880 21 Apr 15 nicklas 382         }
6880 21 Apr 15 nicklas 383         in = new CloseResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
5598 30 Mar 11 nicklas 384       }
6880 21 Apr 15 nicklas 385       finally
6880 21 Apr 15 nicklas 386       {
6880 21 Apr 15 nicklas 387         if (in == null) FileUtil.close(zipFile);
6880 21 Apr 15 nicklas 388       }
5598 30 Mar 11 nicklas 389     }
5598 30 Mar 11 nicklas 390     else
5598 30 Mar 11 nicklas 391     {
5598 30 Mar 11 nicklas 392       in = uri.toURL().openStream();
5598 30 Mar 11 nicklas 393     }
5598 30 Mar 11 nicklas 394     return in;
5598 30 Mar 11 nicklas 395   }
5603 08 Apr 11 nicklas 396
5598 30 Mar 11 nicklas 397   /**
5617 28 Apr 11 nicklas 398     Get an input stream for reading the resource specified by the
5617 28 Apr 11 nicklas 399     given path
5617 28 Apr 11 nicklas 400     @param path The path of the resource
5617 28 Apr 11 nicklas 401     @return An input stream (or null if this extensions file is not a JAR file or if the given path is not found)
5617 28 Apr 11 nicklas 402   */
5617 28 Apr 11 nicklas 403   public InputStream getStream(String path)
5617 28 Apr 11 nicklas 404     throws IOException
5617 28 Apr 11 nicklas 405   {
5617 28 Apr 11 nicklas 406     InputStream in = null;
5617 28 Apr 11 nicklas 407     if (isJar())
5617 28 Apr 11 nicklas 408     {
6880 21 Apr 15 nicklas 409       ZipFile zipFile = null;
6880 21 Apr 15 nicklas 410       try
5617 28 Apr 11 nicklas 411       {
6880 21 Apr 15 nicklas 412         zipFile = new ZipFile(getFile());
6880 21 Apr 15 nicklas 413         ZipEntry zipEntry = zipFile.getEntry(path);
6880 21 Apr 15 nicklas 414         if (zipEntry != null)
6880 21 Apr 15 nicklas 415         {
6880 21 Apr 15 nicklas 416           in = new CloseResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
6880 21 Apr 15 nicklas 417         }
5617 28 Apr 11 nicklas 418       }
6880 21 Apr 15 nicklas 419       finally
6880 21 Apr 15 nicklas 420       {
6880 21 Apr 15 nicklas 421         if (in == null) FileUtil.close(zipFile);
6880 21 Apr 15 nicklas 422       }
5617 28 Apr 11 nicklas 423     }
5617 28 Apr 11 nicklas 424     return in;
5617 28 Apr 11 nicklas 425   }
5617 28 Apr 11 nicklas 426
5617 28 Apr 11 nicklas 427   
5617 28 Apr 11 nicklas 428   /**
5603 08 Apr 11 nicklas 429     Validate the XML file with the extension definitions.
5598 30 Mar 11 nicklas 430   */
5607 15 Apr 11 nicklas 431   synchronized boolean validate()
5598 30 Mar 11 nicklas 432   {
5603 08 Apr 11 nicklas 433     log.info("Validating extensions in file: " + this);
5603 08 Apr 11 nicklas 434     isValid = false;
5607 15 Apr 11 nicklas 435     hasError = false;
5603 08 Apr 11 nicklas 436     validationError = null;
5607 15 Apr 11 nicklas 437     
5603 08 Apr 11 nicklas 438     InputStream in = null;
5603 08 Apr 11 nicklas 439     try
5598 30 Mar 11 nicklas 440     {
5603 08 Apr 11 nicklas 441       in = getXmlStream();
5603 08 Apr 11 nicklas 442       about = new XmlLoader().validateXmlFile(in, getName());
5607 15 Apr 11 nicklas 443       log.info("Successfully validated extensions in file: " + this);
5607 15 Apr 11 nicklas 444       isValid = true;
5598 30 Mar 11 nicklas 445     }
5603 08 Apr 11 nicklas 446     catch (Throwable t)
5603 08 Apr 11 nicklas 447     {
5603 08 Apr 11 nicklas 448       log.error("Error validating extensions in file: " + this, t);
5603 08 Apr 11 nicklas 449       validationError = t;
5603 08 Apr 11 nicklas 450     }
5603 08 Apr 11 nicklas 451     finally
5603 08 Apr 11 nicklas 452     {
5603 08 Apr 11 nicklas 453       FileUtil.close(in);
5603 08 Apr 11 nicklas 454     }
5607 15 Apr 11 nicklas 455     return isValid;
5598 30 Mar 11 nicklas 456   }
5598 30 Mar 11 nicklas 457
5598 30 Mar 11 nicklas 458   
5602 07 Apr 11 nicklas 459   /**
5602 07 Apr 11 nicklas 460     If there was an error when registering the extensions in this file.
5602 07 Apr 11 nicklas 461     This property is only set if the file has been determined to be a 
5602 07 Apr 11 nicklas 462     valid extensions file by the {@link #isValid()} method. Extensions that
5602 07 Apr 11 nicklas 463     has an error are automatically disabled.
5602 07 Apr 11 nicklas 464     
5602 07 Apr 11 nicklas 465     @return TRUE if there was some error, FALSE if everything is ok
5602 07 Apr 11 nicklas 466   */
5602 07 Apr 11 nicklas 467   public boolean hasError()
5602 07 Apr 11 nicklas 468   {
5607 15 Apr 11 nicklas 469     return hasError || !isValid;
5602 07 Apr 11 nicklas 470   }
5602 07 Apr 11 nicklas 471   
5602 07 Apr 11 nicklas 472   /**
5602 07 Apr 11 nicklas 473     Get a list of all objects defined in this extensions
5602 07 Apr 11 nicklas 474     file. This list can contain any type of objects that
5602 07 Apr 11 nicklas 475     have been registered by a processer, but typically
5602 07 Apr 11 nicklas 476     this list contains {@link ExtensionPoint}:s and
5602 07 Apr 11 nicklas 477     {@link Extension}:s. Use {@link #getObjectsOfClass(Class)}
5602 07 Apr 11 nicklas 478     to get a list with only one type of objects.
5602 07 Apr 11 nicklas 479     
5602 07 Apr 11 nicklas 480     @return A list with the objects
5602 07 Apr 11 nicklas 481   */
5602 07 Apr 11 nicklas 482   public List<Object> getAllObjects()
5602 07 Apr 11 nicklas 483   {
5602 07 Apr 11 nicklas 484     List<Object> copy = new ArrayList<Object>();
5602 07 Apr 11 nicklas 485     try
5602 07 Apr 11 nicklas 486     {
5603 08 Apr 11 nicklas 487       if (readLock())
5602 07 Apr 11 nicklas 488       {
5602 07 Apr 11 nicklas 489         copy.addAll(allObjects.values());
5602 07 Apr 11 nicklas 490       }
5602 07 Apr 11 nicklas 491     }
5602 07 Apr 11 nicklas 492     finally
5602 07 Apr 11 nicklas 493     {
5603 08 Apr 11 nicklas 494       rwLock.readLock().unlock();
5602 07 Apr 11 nicklas 495     }
5602 07 Apr 11 nicklas 496     return copy;
5602 07 Apr 11 nicklas 497   }
5602 07 Apr 11 nicklas 498   
5602 07 Apr 11 nicklas 499   /**
5602 07 Apr 11 nicklas 500     Get a list of all objects defined in this extensions
5602 07 Apr 11 nicklas 501     file that are of the specified class or interface.
5602 07 Apr 11 nicklas 502     Note! The returned list may be empty if another
5602 07 Apr 11 nicklas 503     thread is currently processing this file.
5602 07 Apr 11 nicklas 504     
5602 07 Apr 11 nicklas 505     @param ofClass The class/interface the objects must be
5602 07 Apr 11 nicklas 506       an instance of
5602 07 Apr 11 nicklas 507     @return A list with the objects, if no objects are found
5602 07 Apr 11 nicklas 508       the list is empty
5602 07 Apr 11 nicklas 509   */
5602 07 Apr 11 nicklas 510   public <T> List<T> getObjectsOfClass(Class<T> ofClass)
5602 07 Apr 11 nicklas 511   {
5602 07 Apr 11 nicklas 512     List<T> matching = new ArrayList<T>();
5602 07 Apr 11 nicklas 513     try
5602 07 Apr 11 nicklas 514     {
5602 07 Apr 11 nicklas 515       if (readLock())
5602 07 Apr 11 nicklas 516       {
5602 07 Apr 11 nicklas 517         for (Object o : allObjects.values())
5602 07 Apr 11 nicklas 518         {
5602 07 Apr 11 nicklas 519           if (ofClass.isInstance(o)) matching.add(ofClass.cast(o));
5602 07 Apr 11 nicklas 520         }
5602 07 Apr 11 nicklas 521       }
5602 07 Apr 11 nicklas 522     }
5602 07 Apr 11 nicklas 523     finally
5602 07 Apr 11 nicklas 524     {
5603 08 Apr 11 nicklas 525       rwLock.readLock().unlock();
5602 07 Apr 11 nicklas 526     }
5602 07 Apr 11 nicklas 527     return matching;
5602 07 Apr 11 nicklas 528   }
5602 07 Apr 11 nicklas 529   
5602 07 Apr 11 nicklas 530   /**
5602 07 Apr 11 nicklas 531     Get the object that was registered for the given key.
5602 07 Apr 11 nicklas 532     @param key An object key
5602 07 Apr 11 nicklas 533     @return The object or null if no object was found
5602 07 Apr 11 nicklas 534   */
5602 07 Apr 11 nicklas 535   @SuppressWarnings("unchecked")
5602 07 Apr 11 nicklas 536   public <O> O getObjectForKey(ObjectKey<O> key)
5602 07 Apr 11 nicklas 537   {
5602 07 Apr 11 nicklas 538     return (O)allObjects.get(key);
5602 07 Apr 11 nicklas 539   }
5602 07 Apr 11 nicklas 540   
5602 07 Apr 11 nicklas 541   /**
5602 07 Apr 11 nicklas 542     Get metadata registered for a given given key.
5602 07 Apr 11 nicklas 543     @param key An object key
5602 07 Apr 11 nicklas 544     @return A metadata object, or null if no metadata was found
5602 07 Apr 11 nicklas 545   */
5602 07 Apr 11 nicklas 546   @SuppressWarnings("unchecked")
5602 07 Apr 11 nicklas 547   public <M> M getMetadata(ObjectKey<M> key)
5602 07 Apr 11 nicklas 548   {
5602 07 Apr 11 nicklas 549     return (M)objectMetadata.get(key);
5602 07 Apr 11 nicklas 550   }
5602 07 Apr 11 nicklas 551
5605 12 Apr 11 nicklas 552   /**
5605 12 Apr 11 nicklas 553     Get a list of all metadata defined in this extensions
5605 12 Apr 11 nicklas 554     file that are keyed with the specified class or interface.
5605 12 Apr 11 nicklas 555     Note! The returned list may be empty if another
5605 12 Apr 11 nicklas 556     thread is currently processing this file.
5605 12 Apr 11 nicklas 557     
5605 12 Apr 11 nicklas 558     @param ofClass The class/interface the metadata keys must be
5605 12 Apr 11 nicklas 559       an instance of
5605 12 Apr 11 nicklas 560     @return A list with the objects, if no objects are found
5605 12 Apr 11 nicklas 561       the list is empty
5605 12 Apr 11 nicklas 562   */
5605 12 Apr 11 nicklas 563   public <T> List<T> getMetadataKeysOfClass(Class<T> ofClass)
5605 12 Apr 11 nicklas 564   {
5605 12 Apr 11 nicklas 565     List<T> matching = new ArrayList<T>();
5605 12 Apr 11 nicklas 566     try
5605 12 Apr 11 nicklas 567     {
5605 12 Apr 11 nicklas 568       if (readLock())
5605 12 Apr 11 nicklas 569       {
6875 20 Apr 15 nicklas 570         for (ObjectKey<?> key : objectMetadata.keySet())
5605 12 Apr 11 nicklas 571         {
5605 12 Apr 11 nicklas 572           if (ofClass.isInstance(key)) matching.add(ofClass.cast(key));
5605 12 Apr 11 nicklas 573         }
5605 12 Apr 11 nicklas 574       }
5605 12 Apr 11 nicklas 575     }
5605 12 Apr 11 nicklas 576     finally
5605 12 Apr 11 nicklas 577     {
5605 12 Apr 11 nicklas 578       rwLock.readLock().unlock();
5605 12 Apr 11 nicklas 579     }
5605 12 Apr 11 nicklas 580     return matching;
5605 12 Apr 11 nicklas 581   }
5605 12 Apr 11 nicklas 582   
5607 15 Apr 11 nicklas 583   /**
5607 15 Apr 11 nicklas 584     Get the class loader used to load classes for the extension. Only JAR
7227 15 Nov 16 nicklas 585     files have class loaders so this method may return null. Once a class
7227 15 Nov 16 nicklas 586     loader has been created it remains the same until a change has been detected
7227 15 Nov 16 nicklas 587     with {@link #checkModified()} which forces the creation of a new class loader
7227 15 Nov 16 nicklas 588     when this method is called.
5607 15 Apr 11 nicklas 589   */
5598 30 Mar 11 nicklas 590   public ClassLoader getClassLoader()
5600 01 Apr 11 nicklas 591     throws IOException
5598 30 Mar 11 nicklas 592   {
7228 16 Nov 16 nicklas 593     if ((jarLoader == null) && isJar())
5600 01 Apr 11 nicklas 594     {
5607 15 Apr 11 nicklas 595       jarLoader = (JarClassLoader)JarClassLoader.getInstance(getFile().getAbsolutePath(), true);
5600 01 Apr 11 nicklas 596     }
5598 30 Mar 11 nicklas 597     return jarLoader;
5598 30 Mar 11 nicklas 598   }
5598 30 Mar 11 nicklas 599
5602 07 Apr 11 nicklas 600   /**
5602 07 Apr 11 nicklas 601     Get a writeable view for the current file. Note that the view
5602 07 Apr 11 nicklas 602     is returned in read-only mode and to really be able to write information
5602 07 Apr 11 nicklas 603     the method {@link WriteableExtensionsFile#open()} must be called.
5602 07 Apr 11 nicklas 604   */
5602 07 Apr 11 nicklas 605   WriteableExtensionsFile getWriteableFile()
5602 07 Apr 11 nicklas 606   {
5602 07 Apr 11 nicklas 607     return new WriteableExtensionsFile(this);
5602 07 Apr 11 nicklas 608   }
5598 30 Mar 11 nicklas 609   
5602 07 Apr 11 nicklas 610   /**
5602 07 Apr 11 nicklas 611     Try to aquire a read-lock. A read-lock is needed when reading information
5602 07 Apr 11 nicklas 612     that may be corrupted if another thread is currently writing information
5602 07 Apr 11 nicklas 613     to this file.
5602 07 Apr 11 nicklas 614     @return TRUE if the read-lock could be aquired immediately, FALSE if not
5602 07 Apr 11 nicklas 615   */
5602 07 Apr 11 nicklas 616   boolean readLock()
5602 07 Apr 11 nicklas 617   {
5602 07 Apr 11 nicklas 618     return rwLock.readLock().tryLock();
5602 07 Apr 11 nicklas 619   }
5602 07 Apr 11 nicklas 620   
5602 07 Apr 11 nicklas 621   /**
5602 07 Apr 11 nicklas 622     Try to aquire a write-lock. A write-lock is needed by any thread that wants
5602 07 Apr 11 nicklas 623     to update information about this file. The write-lock can only be held by
5602 07 Apr 11 nicklas 624     a single thread at a time.
5602 07 Apr 11 nicklas 625     @return TRUE if the write-lock could be aquired within 1 second, FALSE if not
5602 07 Apr 11 nicklas 626   */
5602 07 Apr 11 nicklas 627   boolean writeLock()
5602 07 Apr 11 nicklas 628   {
5602 07 Apr 11 nicklas 629     try
5602 07 Apr 11 nicklas 630     {
5602 07 Apr 11 nicklas 631       return rwLock.writeLock().tryLock(1, TimeUnit.SECONDS);
5602 07 Apr 11 nicklas 632     }
5602 07 Apr 11 nicklas 633     catch (InterruptedException ex)
5602 07 Apr 11 nicklas 634     {}
5602 07 Apr 11 nicklas 635     return false;
5602 07 Apr 11 nicklas 636   }
5602 07 Apr 11 nicklas 637   
5602 07 Apr 11 nicklas 638   /**
5602 07 Apr 11 nicklas 639     An extensions file with additional methods that allows adding
5602 07 Apr 11 nicklas 640     or modifying information in the underlying extensions file. This kind of 
5602 07 Apr 11 nicklas 641     view is usually handed out to {@link ExtensionsFileProcessor} implementations
5602 07 Apr 11 nicklas 642     as a result of calling {@link ExtensionsManager#processFiles(ExtensionsFileProcessor)}.
5602 07 Apr 11 nicklas 643     <p>
5602 07 Apr 11 nicklas 644     
5602 07 Apr 11 nicklas 645     Note that the file starts out in read-only mode and that
5602 07 Apr 11 nicklas 646     the processor has to call {@link #open()} to aquire a write-lock
5602 07 Apr 11 nicklas 647     before calling any writing methods. It is recommended that
5602 07 Apr 11 nicklas 648     the lock is released as soon as possible by calling {@link #close()}.
5602 07 Apr 11 nicklas 649   */
5602 07 Apr 11 nicklas 650   public static class WriteableExtensionsFile
5602 07 Apr 11 nicklas 651   {
5602 07 Apr 11 nicklas 652     private final ExtensionsFile xtFile;
5602 07 Apr 11 nicklas 653     private boolean isClosed;
5602 07 Apr 11 nicklas 654     
5602 07 Apr 11 nicklas 655     /**
5602 07 Apr 11 nicklas 656       Creates a new writeable extensions file. 
5602 07 Apr 11 nicklas 657     */
5602 07 Apr 11 nicklas 658     WriteableExtensionsFile(ExtensionsFile xtFile)
5602 07 Apr 11 nicklas 659     {
5602 07 Apr 11 nicklas 660       this.xtFile = xtFile;
5602 07 Apr 11 nicklas 661       this.isClosed = true;
5602 07 Apr 11 nicklas 662     }
5602 07 Apr 11 nicklas 663     
5602 07 Apr 11 nicklas 664     /**
5602 07 Apr 11 nicklas 665       Get the underlying extensions file (for readin information)
5602 07 Apr 11 nicklas 666     */
5602 07 Apr 11 nicklas 667     public ExtensionsFile getExtensionsFile()
5602 07 Apr 11 nicklas 668     {
5602 07 Apr 11 nicklas 669       return xtFile;
5602 07 Apr 11 nicklas 670     }
5602 07 Apr 11 nicklas 671     
5602 07 Apr 11 nicklas 672     /**
5602 07 Apr 11 nicklas 673        Close the writeable file. This will release the lock and
5602 07 Apr 11 nicklas 674        any further write operations are disallowed.
5602 07 Apr 11 nicklas 675     */
5602 07 Apr 11 nicklas 676     public void close()
5602 07 Apr 11 nicklas 677     {
5602 07 Apr 11 nicklas 678       if (isClosed) return;
5602 07 Apr 11 nicklas 679       isClosed = true;
5602 07 Apr 11 nicklas 680       xtFile.rwLock.writeLock().unlock();
5602 07 Apr 11 nicklas 681     }
5602 07 Apr 11 nicklas 682     
5602 07 Apr 11 nicklas 683     public boolean open()
5602 07 Apr 11 nicklas 684     {
5602 07 Apr 11 nicklas 685       if (isClosed)
5602 07 Apr 11 nicklas 686       {
5602 07 Apr 11 nicklas 687         isClosed = !xtFile.writeLock();
5602 07 Apr 11 nicklas 688       }
5602 07 Apr 11 nicklas 689       return !isClosed;
5602 07 Apr 11 nicklas 690     }
5602 07 Apr 11 nicklas 691     
5602 07 Apr 11 nicklas 692     /**
5602 07 Apr 11 nicklas 693       Check if the file has been closed and throws an
5602 07 Apr 11 nicklas 694       IllegalStateException if it has.
5602 07 Apr 11 nicklas 695     */
5602 07 Apr 11 nicklas 696     private void checkClosed()
5602 07 Apr 11 nicklas 697     {
5602 07 Apr 11 nicklas 698       if (isClosed) 
5602 07 Apr 11 nicklas 699       {
5602 07 Apr 11 nicklas 700         throw new IllegalStateException("The file has been closed: " + xtFile);
5602 07 Apr 11 nicklas 701       }
5602 07 Apr 11 nicklas 702     }
5602 07 Apr 11 nicklas 703     
5602 07 Apr 11 nicklas 704     /**
5603 08 Apr 11 nicklas 705       Mark the file as fully processed by the extensions manager. 
5603 08 Apr 11 nicklas 706     */
5603 08 Apr 11 nicklas 707     public void markAsProcessed()
5603 08 Apr 11 nicklas 708     {
5603 08 Apr 11 nicklas 709       xtFile.isNew = false;
5603 08 Apr 11 nicklas 710       xtFile.wasModified = false;
5603 08 Apr 11 nicklas 711       if (xtFile.file != null)
5603 08 Apr 11 nicklas 712       {
5603 08 Apr 11 nicklas 713         xtFile.lastModified = xtFile.file.lastModified();
5603 08 Apr 11 nicklas 714         xtFile.lastLength = xtFile.file.length();
5603 08 Apr 11 nicklas 715       }
5607 15 Apr 11 nicklas 716       if (!xtFile.isValid)
5607 15 Apr 11 nicklas 717       {
5607 15 Apr 11 nicklas 718         // Cleanup to avoid leaking memory
5607 15 Apr 11 nicklas 719         xtFile.allObjects.clear();
5616 27 Apr 11 nicklas 720         xtFile.manager.unregisterAllObjects(xtFile);
5616 27 Apr 11 nicklas 721         xtFile.objectMetadata.clear();
5607 15 Apr 11 nicklas 722         xtFile.jarLoader = null;
5607 15 Apr 11 nicklas 723       }
5603 08 Apr 11 nicklas 724     }
5603 08 Apr 11 nicklas 725     
5603 08 Apr 11 nicklas 726     /**
5616 27 Apr 11 nicklas 727       Mark the file as uninstalled.
5616 27 Apr 11 nicklas 728     */
5616 27 Apr 11 nicklas 729     public void setUninstalled()
5616 27 Apr 11 nicklas 730     {
5616 27 Apr 11 nicklas 731       xtFile.manager.removeFile(xtFile.file);
5616 27 Apr 11 nicklas 732     }
5616 27 Apr 11 nicklas 733     
5616 27 Apr 11 nicklas 734     /**
5616 27 Apr 11 nicklas 735       Mark the file as installed.
5616 27 Apr 11 nicklas 736     */
5616 27 Apr 11 nicklas 737     public void setInstalled()
5616 27 Apr 11 nicklas 738     {
5616 27 Apr 11 nicklas 739       xtFile.manager.getSettings().setInstalledFile(xtFile.file);
5616 27 Apr 11 nicklas 740     }
5616 27 Apr 11 nicklas 741     
5616 27 Apr 11 nicklas 742     /**
5602 07 Apr 11 nicklas 743       Sets the error status. This method can be called even on closed
5602 07 Apr 11 nicklas 744       files, since it is important to error handling.
5602 07 Apr 11 nicklas 745     */
5602 07 Apr 11 nicklas 746     public void setError(boolean error)
5602 07 Apr 11 nicklas 747     {
5602 07 Apr 11 nicklas 748       xtFile.hasError = error;
5602 07 Apr 11 nicklas 749     }
7228 16 Nov 16 nicklas 750     
7228 16 Nov 16 nicklas 751     /**
7228 16 Nov 16 nicklas 752       Reset the classloader for this file, forcing a new
7228 16 Nov 16 nicklas 753       instance in case the JAR file has been changed. 
7228 16 Nov 16 nicklas 754       This method should be called before re-installing 
7228 16 Nov 16 nicklas 755       an extension and the {@link ExtensionsFile#checkModified()} 
7228 16 Nov 16 nicklas 756       returns true.
7228 16 Nov 16 nicklas 757       @throws IllegalStateException If the file has been closed
7228 16 Nov 16 nicklas 758       @since 3.10
7228 16 Nov 16 nicklas 759     */
7228 16 Nov 16 nicklas 760     public void resetClassLoader()
7228 16 Nov 16 nicklas 761     {
7228 16 Nov 16 nicklas 762       checkClosed();
7273 20 Jan 17 nicklas 763       if (xtFile.jarLoader != null)
7273 20 Jan 17 nicklas 764       {
7273 20 Jan 17 nicklas 765         xtFile.manager.getRegistry().unregisterEventHandlers(xtFile.jarLoader);
7273 20 Jan 17 nicklas 766         xtFile.jarLoader = null;
7273 20 Jan 17 nicklas 767       }
7228 16 Nov 16 nicklas 768     }
5602 07 Apr 11 nicklas 769
5602 07 Apr 11 nicklas 770     /**
5602 07 Apr 11 nicklas 771       Register an object as "defined" by this extensions file.
5602 07 Apr 11 nicklas 772       @param key The object key used to identify the object
5602 07 Apr 11 nicklas 773       @param obj The object to register
5602 07 Apr 11 nicklas 774       @throws IllegalStateException If the file has been closed
5602 07 Apr 11 nicklas 775     */
5602 07 Apr 11 nicklas 776     public <O> void registerObject(ObjectKey<O> key, O obj)
5602 07 Apr 11 nicklas 777     {
5602 07 Apr 11 nicklas 778       checkClosed();
5606 14 Apr 11 nicklas 779       xtFile.manager.registerObject(key, this.xtFile);
5602 07 Apr 11 nicklas 780       xtFile.allObjects.put(key, obj);
5602 07 Apr 11 nicklas 781     }
5602 07 Apr 11 nicklas 782     
5602 07 Apr 11 nicklas 783     /**
5605 12 Apr 11 nicklas 784       Unregister an object that was "defined" by this extensions
5605 12 Apr 11 nicklas 785       file.
5605 12 Apr 11 nicklas 786       @param key The object key used to identify the object
5605 12 Apr 11 nicklas 787       @throws IllegalStateException If the file has been closed
5605 12 Apr 11 nicklas 788     */
5605 12 Apr 11 nicklas 789     public <O> void unregisterObject(ObjectKey<O> key)
5605 12 Apr 11 nicklas 790     {
5605 12 Apr 11 nicklas 791       checkClosed();
5605 12 Apr 11 nicklas 792       xtFile.allObjects.remove(key);
5606 14 Apr 11 nicklas 793       xtFile.manager.unregisterObject(key, this.xtFile);
5605 12 Apr 11 nicklas 794     }
5605 12 Apr 11 nicklas 795     
5605 12 Apr 11 nicklas 796     /**
5602 07 Apr 11 nicklas 797       Register metadata about an object. The difference
5602 07 Apr 11 nicklas 798       between this method and the {@link #registerObject(ObjectKey, Object)}
5602 07 Apr 11 nicklas 799       method is that the <code>registerObject</code> method
5602 07 Apr 11 nicklas 800       also registers the objects with the {@link ExtensionsManager}.
5602 07 Apr 11 nicklas 801       
5602 07 Apr 11 nicklas 802       @param key The object key used to identify the metadata
5602 07 Apr 11 nicklas 803       @param metadata The metadata to store under the key
5602 07 Apr 11 nicklas 804     */
5602 07 Apr 11 nicklas 805     public <M> void registerMetadata(ObjectKey<M> key, M metadata)
5602 07 Apr 11 nicklas 806     {
5602 07 Apr 11 nicklas 807       checkClosed();
5602 07 Apr 11 nicklas 808       xtFile.objectMetadata.put(key, metadata);
5602 07 Apr 11 nicklas 809     }
5602 07 Apr 11 nicklas 810     
5602 07 Apr 11 nicklas 811     
5605 12 Apr 11 nicklas 812     /**
5605 12 Apr 11 nicklas 813       Unregister metadata about an object. 
5605 12 Apr 11 nicklas 814       @param key The object key used to identify the metadata
5605 12 Apr 11 nicklas 815     */
5605 12 Apr 11 nicklas 816     public <M> void unregisterMetadata(ObjectKey<M> key)
5605 12 Apr 11 nicklas 817     {
5605 12 Apr 11 nicklas 818       checkClosed();
5605 12 Apr 11 nicklas 819       xtFile.objectMetadata.remove(key);
5605 12 Apr 11 nicklas 820     }
5605 12 Apr 11 nicklas 821     
5602 07 Apr 11 nicklas 822   }
5598 30 Mar 11 nicklas 823 }