5598 |
30 Mar 11 |
nicklas |
1 |
/** |
5598 |
30 Mar 11 |
nicklas |
$Id$ |
5598 |
30 Mar 11 |
nicklas |
3 |
|
5598 |
30 Mar 11 |
nicklas |
Copyright (C) 2011 Nicklas Nordborg |
5598 |
30 Mar 11 |
nicklas |
5 |
|
5598 |
30 Mar 11 |
nicklas |
This file is part of BASE - BioArray Software Environment. |
5598 |
30 Mar 11 |
nicklas |
Available at http://base.thep.lu.se/ |
5598 |
30 Mar 11 |
nicklas |
8 |
|
5598 |
30 Mar 11 |
nicklas |
BASE is free software; you can redistribute it and/or |
5598 |
30 Mar 11 |
nicklas |
modify it under the terms of the GNU General Public License |
5598 |
30 Mar 11 |
nicklas |
as published by the Free Software Foundation; either version 3 |
5598 |
30 Mar 11 |
nicklas |
of the License, or (at your option) any later version. |
5598 |
30 Mar 11 |
nicklas |
13 |
|
5598 |
30 Mar 11 |
nicklas |
BASE is distributed in the hope that it will be useful, |
5598 |
30 Mar 11 |
nicklas |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
5598 |
30 Mar 11 |
nicklas |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
5598 |
30 Mar 11 |
nicklas |
GNU General Public License for more details. |
5598 |
30 Mar 11 |
nicklas |
18 |
|
5598 |
30 Mar 11 |
nicklas |
You should have received a copy of the GNU General Public License |
5598 |
30 Mar 11 |
nicklas |
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 |
Represents a file with extensions in it. The file is either an XML file |
5598 |
30 Mar 11 |
nicklas |
or a JAR file. A JAR file is always a real file on the file system and |
5598 |
30 Mar 11 |
nicklas |
can be accessed with {@link #getFile()}. An XML file can be both a real file |
5598 |
30 Mar 11 |
nicklas |
or an abstract stream of bytes and can be accessed by {@link #getXmlStream()}. |
5598 |
30 Mar 11 |
nicklas |
The latter method can also be used in the case of a JAR file and will then |
5598 |
30 Mar 11 |
nicklas |
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 |
@author Nicklas |
5598 |
30 Mar 11 |
nicklas |
@since 3.0 |
5598 |
30 Mar 11 |
nicklas |
@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 |
Create a new extensions file backed by a real file on the |
5598 |
30 Mar 11 |
nicklas |
file system. If the file name ends with '.jar' it is assumed |
5598 |
30 Mar 11 |
nicklas |
to be a JAR file, otherwise it must be an XML file containing |
5598 |
30 Mar 11 |
nicklas |
extension definitions. |
5598 |
30 Mar 11 |
nicklas |
@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 |
Create a new extensions file backed by a real file |
5598 |
30 Mar 11 |
nicklas |
on the file system. The file may be a JAR file or an |
5598 |
30 Mar 11 |
nicklas |
XML file. |
5598 |
30 Mar 11 |
nicklas |
102 |
|
5598 |
30 Mar 11 |
nicklas |
@param file The file |
5598 |
30 Mar 11 |
nicklas |
@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 |
Create a new extension file backed by an URI. The URI must point to |
5598 |
30 Mar 11 |
nicklas |
an XML file containing extension definitions. |
5598 |
30 Mar 11 |
nicklas |
@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 |
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 |
// 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 |
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 |
Get the name of the file. The name may or may not |
5598 |
30 Mar 11 |
nicklas |
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 |
Get the underlying file on the file system. Return null |
5598 |
30 Mar 11 |
nicklas |
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 |
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 |
If the file name ends with '.jar' the file is considered a JAR file. |
5598 |
30 Mar 11 |
nicklas |
Otherwise it is considered an XML file. The file name check is case |
5598 |
30 Mar 11 |
nicklas |
sensitive. |
5598 |
30 Mar 11 |
nicklas |
@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 |
Check if the file exists. For files that are only represented by |
5603 |
08 Apr 11 |
nicklas |
an URI this method always return true. For other files the call |
5823 |
24 Oct 11 |
nicklas |
is forwarded to {@link java.io.File#exists()} and {@link java.io.File#isFile()}. |
5823 |
24 Oct 11 |
nicklas |
@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 |
If the file is a new file not previously processed by the |
5603 |
08 Apr 11 |
nicklas |
extension system. Thew 'new' status is changed when |
5603 |
08 Apr 11 |
nicklas |
{@link WriteableExtensionsFile#markAsProcessed()} is called which |
5603 |
08 Apr 11 |
nicklas |
usually happens when all extensions has been registered. |
5603 |
08 Apr 11 |
nicklas |
@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 |
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 |
Is this an extension file that should be ignored? |
7232 |
17 Nov 16 |
nicklas |
@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 |
Check if the file was modified when the last call to |
5603 |
08 Apr 11 |
nicklas |
{@link #checkModified()} was made. It is recommended |
5603 |
08 Apr 11 |
nicklas |
that processor implementation use this method instead |
5603 |
08 Apr 11 |
nicklas |
to avoid inconsistent behaviour if the file happens to |
5603 |
08 Apr 11 |
nicklas |
be modified while a processor is running. |
5603 |
08 Apr 11 |
nicklas |
254 |
|
5603 |
08 Apr 11 |
nicklas |
@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 |
Check if the underlying file has been modified since it was |
5603 |
08 Apr 11 |
nicklas |
last processed. Files that are new are always considered to be |
5603 |
08 Apr 11 |
nicklas |
modified. Files that are only represented as an URI are considered |
5603 |
08 Apr 11 |
nicklas |
to be unmodifiable. For other files, we check the |
5823 |
24 Oct 11 |
nicklas |
{@link java.io.File#lastModified()} and {@link java.io.File#length()} of the |
5603 |
08 Apr 11 |
nicklas |
underlying file and compare that to the last known timestamp and size. |
5603 |
08 Apr 11 |
nicklas |
Files that are JAR files we also check with the class loader if any of |
5603 |
08 Apr 11 |
nicklas |
the libraries it uses have changed. |
5603 |
08 Apr 11 |
nicklas |
<p> |
5603 |
08 Apr 11 |
nicklas |
The result of this call is remembered until this method is called |
5603 |
08 Apr 11 |
nicklas |
again or until the file is marked as processed. |
5603 |
08 Apr 11 |
nicklas |
274 |
|
5603 |
08 Apr 11 |
nicklas |
@see WriteableExtensionsFile#markAsProcessed() |
5603 |
08 Apr 11 |
nicklas |
@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 |
// 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 |
// 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 |
// 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 |
Is the file a valid extensions file according to the last |
5603 |
08 Apr 11 |
nicklas |
performed validation. |
5603 |
08 Apr 11 |
nicklas |
310 |
|
5603 |
08 Apr 11 |
nicklas |
This means that the file must be either: |
5603 |
08 Apr 11 |
nicklas |
<ul> |
5603 |
08 Apr 11 |
nicklas |
<li>An extensions definition XML file |
5603 |
08 Apr 11 |
nicklas |
<li>A JAR file with an extensions definition XML file at |
5603 |
08 Apr 11 |
nicklas |
<code>META-INF/extensions.xml</code>. |
5603 |
08 Apr 11 |
nicklas |
</ul> |
5603 |
08 Apr 11 |
nicklas |
317 |
|
5603 |
08 Apr 11 |
nicklas |
The result of the validation is remembered as long as the |
5603 |
08 Apr 11 |
nicklas |
file is not modified. If {@link #checkModified()} returns |
5603 |
08 Apr 11 |
nicklas |
true, the return value of this method may be out of sync. |
5603 |
08 Apr 11 |
nicklas |
Re-validation of modified files are usually performed |
5603 |
08 Apr 11 |
nicklas |
by the extensions manager by calling |
5691 |
11 Aug 11 |
nicklas |
{@link ExtensionsManager#scanForChanges()} |
5603 |
08 Apr 11 |
nicklas |
324 |
|
5603 |
08 Apr 11 |
nicklas |
<p> |
5603 |
08 Apr 11 |
nicklas |
NOTE! The validation will only verify that the XML file |
5603 |
08 Apr 11 |
nicklas |
follows the rules defined by the schema definition. It will |
5603 |
08 Apr 11 |
nicklas |
not check that actual classes, etc. exists and can be created. |
5603 |
08 Apr 11 |
nicklas |
This usually happens later in the registration process, and |
5603 |
08 Apr 11 |
nicklas |
can be checked by {@link #hasError()}. |
5603 |
08 Apr 11 |
nicklas |
331 |
|
5603 |
08 Apr 11 |
nicklas |
@return TRUE if the file is valid, FALSE otherwise |
5603 |
08 Apr 11 |
nicklas |
@see #getValidationError() |
5603 |
08 Apr 11 |
nicklas |
@see #checkModified() |
5603 |
08 Apr 11 |
nicklas |
@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 |
Get more information about the error that caused the validation |
5603 |
08 Apr 11 |
nicklas |
to fail. |
5603 |
08 Apr 11 |
nicklas |
@return An exception or null if the validation succeeded |
5603 |
08 Apr 11 |
nicklas |
@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 |
Get information about the extensions in this file. |
5603 |
08 Apr 11 |
nicklas |
@return An About object or null if the file is not a valid |
5603 |
08 Apr 11 |
nicklas |
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 |
Get an input stream for reading the XML file containing the extension |
5603 |
08 Apr 11 |
nicklas |
definitions. |
5603 |
08 Apr 11 |
nicklas |
@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 |
Get an input stream for reading the resource specified by the |
5617 |
28 Apr 11 |
nicklas |
given path |
5617 |
28 Apr 11 |
nicklas |
@param path The path of the resource |
5617 |
28 Apr 11 |
nicklas |
@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 |
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 |
If there was an error when registering the extensions in this file. |
5602 |
07 Apr 11 |
nicklas |
This property is only set if the file has been determined to be a |
5602 |
07 Apr 11 |
nicklas |
valid extensions file by the {@link #isValid()} method. Extensions that |
5602 |
07 Apr 11 |
nicklas |
has an error are automatically disabled. |
5602 |
07 Apr 11 |
nicklas |
464 |
|
5602 |
07 Apr 11 |
nicklas |
@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 |
Get a list of all objects defined in this extensions |
5602 |
07 Apr 11 |
nicklas |
file. This list can contain any type of objects that |
5602 |
07 Apr 11 |
nicklas |
have been registered by a processer, but typically |
5602 |
07 Apr 11 |
nicklas |
this list contains {@link ExtensionPoint}:s and |
5602 |
07 Apr 11 |
nicklas |
{@link Extension}:s. Use {@link #getObjectsOfClass(Class)} |
5602 |
07 Apr 11 |
nicklas |
to get a list with only one type of objects. |
5602 |
07 Apr 11 |
nicklas |
479 |
|
5602 |
07 Apr 11 |
nicklas |
@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 |
Get a list of all objects defined in this extensions |
5602 |
07 Apr 11 |
nicklas |
file that are of the specified class or interface. |
5602 |
07 Apr 11 |
nicklas |
Note! The returned list may be empty if another |
5602 |
07 Apr 11 |
nicklas |
thread is currently processing this file. |
5602 |
07 Apr 11 |
nicklas |
504 |
|
5602 |
07 Apr 11 |
nicklas |
@param ofClass The class/interface the objects must be |
5602 |
07 Apr 11 |
nicklas |
an instance of |
5602 |
07 Apr 11 |
nicklas |
@return A list with the objects, if no objects are found |
5602 |
07 Apr 11 |
nicklas |
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 |
Get the object that was registered for the given key. |
5602 |
07 Apr 11 |
nicklas |
@param key An object key |
5602 |
07 Apr 11 |
nicklas |
@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 |
Get metadata registered for a given given key. |
5602 |
07 Apr 11 |
nicklas |
@param key An object key |
5602 |
07 Apr 11 |
nicklas |
@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 |
Get a list of all metadata defined in this extensions |
5605 |
12 Apr 11 |
nicklas |
file that are keyed with the specified class or interface. |
5605 |
12 Apr 11 |
nicklas |
Note! The returned list may be empty if another |
5605 |
12 Apr 11 |
nicklas |
thread is currently processing this file. |
5605 |
12 Apr 11 |
nicklas |
557 |
|
5605 |
12 Apr 11 |
nicklas |
@param ofClass The class/interface the metadata keys must be |
5605 |
12 Apr 11 |
nicklas |
an instance of |
5605 |
12 Apr 11 |
nicklas |
@return A list with the objects, if no objects are found |
5605 |
12 Apr 11 |
nicklas |
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 |
Get the class loader used to load classes for the extension. Only JAR |
7227 |
15 Nov 16 |
nicklas |
files have class loaders so this method may return null. Once a class |
7227 |
15 Nov 16 |
nicklas |
loader has been created it remains the same until a change has been detected |
7227 |
15 Nov 16 |
nicklas |
with {@link #checkModified()} which forces the creation of a new class loader |
7227 |
15 Nov 16 |
nicklas |
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 |
Get a writeable view for the current file. Note that the view |
5602 |
07 Apr 11 |
nicklas |
is returned in read-only mode and to really be able to write information |
5602 |
07 Apr 11 |
nicklas |
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 |
Try to aquire a read-lock. A read-lock is needed when reading information |
5602 |
07 Apr 11 |
nicklas |
that may be corrupted if another thread is currently writing information |
5602 |
07 Apr 11 |
nicklas |
to this file. |
5602 |
07 Apr 11 |
nicklas |
@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 |
Try to aquire a write-lock. A write-lock is needed by any thread that wants |
5602 |
07 Apr 11 |
nicklas |
to update information about this file. The write-lock can only be held by |
5602 |
07 Apr 11 |
nicklas |
a single thread at a time. |
5602 |
07 Apr 11 |
nicklas |
@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 |
An extensions file with additional methods that allows adding |
5602 |
07 Apr 11 |
nicklas |
or modifying information in the underlying extensions file. This kind of |
5602 |
07 Apr 11 |
nicklas |
view is usually handed out to {@link ExtensionsFileProcessor} implementations |
5602 |
07 Apr 11 |
nicklas |
as a result of calling {@link ExtensionsManager#processFiles(ExtensionsFileProcessor)}. |
5602 |
07 Apr 11 |
nicklas |
<p> |
5602 |
07 Apr 11 |
nicklas |
644 |
|
5602 |
07 Apr 11 |
nicklas |
Note that the file starts out in read-only mode and that |
5602 |
07 Apr 11 |
nicklas |
the processor has to call {@link #open()} to aquire a write-lock |
5602 |
07 Apr 11 |
nicklas |
before calling any writing methods. It is recommended that |
5602 |
07 Apr 11 |
nicklas |
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 |
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 |
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 |
Close the writeable file. This will release the lock and |
5602 |
07 Apr 11 |
nicklas |
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 |
Check if the file has been closed and throws an |
5602 |
07 Apr 11 |
nicklas |
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 |
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 |
// 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 |
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 |
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 |
Sets the error status. This method can be called even on closed |
5602 |
07 Apr 11 |
nicklas |
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 |
Reset the classloader for this file, forcing a new |
7228 |
16 Nov 16 |
nicklas |
instance in case the JAR file has been changed. |
7228 |
16 Nov 16 |
nicklas |
This method should be called before re-installing |
7228 |
16 Nov 16 |
nicklas |
an extension and the {@link ExtensionsFile#checkModified()} |
7228 |
16 Nov 16 |
nicklas |
returns true. |
7228 |
16 Nov 16 |
nicklas |
@throws IllegalStateException If the file has been closed |
7228 |
16 Nov 16 |
nicklas |
@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 |
Register an object as "defined" by this extensions file. |
5602 |
07 Apr 11 |
nicklas |
@param key The object key used to identify the object |
5602 |
07 Apr 11 |
nicklas |
@param obj The object to register |
5602 |
07 Apr 11 |
nicklas |
@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 |
Unregister an object that was "defined" by this extensions |
5605 |
12 Apr 11 |
nicklas |
file. |
5605 |
12 Apr 11 |
nicklas |
@param key The object key used to identify the object |
5605 |
12 Apr 11 |
nicklas |
@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 |
Register metadata about an object. The difference |
5602 |
07 Apr 11 |
nicklas |
between this method and the {@link #registerObject(ObjectKey, Object)} |
5602 |
07 Apr 11 |
nicklas |
method is that the <code>registerObject</code> method |
5602 |
07 Apr 11 |
nicklas |
also registers the objects with the {@link ExtensionsManager}. |
5602 |
07 Apr 11 |
nicklas |
801 |
|
5602 |
07 Apr 11 |
nicklas |
@param key The object key used to identify the metadata |
5602 |
07 Apr 11 |
nicklas |
@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 |
Unregister metadata about an object. |
5605 |
12 Apr 11 |
nicklas |
@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 |
} |