Lines 30-36
Link Here
|
30 |
import org.openide.filesystems.FileObject; |
30 |
import org.openide.filesystems.FileObject; |
31 |
import org.openide.filesystems.FileStateInvalidException; |
31 |
import org.openide.filesystems.FileStateInvalidException; |
32 |
import org.openide.modules.InstalledFileLocator; |
32 |
import org.openide.modules.InstalledFileLocator; |
|
|
33 |
import org.openide.modules.ModuleInfo; |
33 |
import org.openide.util.*; |
34 |
import org.openide.util.*; |
|
|
35 |
import org.openide.xml.XMLUtil; |
36 |
import org.w3c.dom.Document; |
37 |
import org.w3c.dom.Element; |
38 |
import org.w3c.dom.Node; |
39 |
import org.w3c.dom.NodeList; |
40 |
import org.xml.sax.InputSource; |
41 |
import org.xml.sax.SAXException; |
34 |
|
42 |
|
35 |
/** |
43 |
/** |
36 |
* Utility class providing entry points to the bridging functionality. |
44 |
* Utility class providing entry points to the bridging functionality. |
Lines 41-73
Link Here
|
41 |
private AntBridge() {} |
49 |
private AntBridge() {} |
42 |
|
50 |
|
43 |
private static final String KEY_MAIN_CLASS_LOADER = "mainClassLoader"; // NOI18N |
51 |
private static final String KEY_MAIN_CLASS_LOADER = "mainClassLoader"; // NOI18N |
44 |
private static final String KEY_AUX_CLASS_LOADER = "auxClassLoader"; // NOI18N |
52 |
private static final String KEY_BRIDGE_CLASS_LOADER = "bridgeClassLoader"; // NOI18N |
45 |
private static final String KEY_BRIDGE = "bridge"; // NOI18N |
53 |
private static final String KEY_BRIDGE = "bridge"; // NOI18N |
46 |
private static final String KEY_CUSTOM_DEFS = "customDefs"; |
54 |
private static final String KEY_CUSTOM_DEFS = "customDefs"; // NOI18N |
|
|
55 |
private static final String KEY_CUSTOM_DEF_CLASS_LOADERS = "customDefClassLoaders"; // NOI18N |
47 |
private static Reference stuff = null; // Reference<Map> |
56 |
private static Reference stuff = null; // Reference<Map> |
48 |
|
57 |
|
49 |
private static List listeners = new ArrayList(); // List<ChangeListener> |
58 |
private static List listeners = new ArrayList(); // List<ChangeListener> |
50 |
|
59 |
|
51 |
private static final class MiscListener implements PropertyChangeListener, LookupListener { |
60 |
private static final class MiscListener implements PropertyChangeListener, LookupListener { |
|
|
61 |
MiscListener() {} |
62 |
private ModuleInfo[] modules = null; |
52 |
public void propertyChange(PropertyChangeEvent ev) { |
63 |
public void propertyChange(PropertyChangeEvent ev) { |
53 |
String prop = ev.getPropertyName(); |
64 |
String prop = ev.getPropertyName(); |
54 |
if (prop == null || |
65 |
if (AntSettings.PROP_ANT_HOME.equals(prop) || |
55 |
AntSettings.PROP_ANT_HOME.equals(prop) || |
|
|
56 |
AntSettings.PROP_EXTRA_CLASSPATH.equals(prop)) { |
66 |
AntSettings.PROP_EXTRA_CLASSPATH.equals(prop)) { |
57 |
AntModule.err.log("AntBridge got settings change in " + prop); |
67 |
AntModule.err.log("AntBridge got settings change in " + prop); |
58 |
fireChange(); |
68 |
fireChange(); |
|
|
69 |
} else if (ModuleInfo.PROP_ENABLED.equals(prop)) { |
70 |
AntModule.err.log("AntBridge got module enablement change on " + ev.getSource()); |
71 |
fireChange(); |
59 |
} |
72 |
} |
60 |
} |
73 |
} |
61 |
public void resultChanged(LookupEvent ev) { |
74 |
public void resultChanged(LookupEvent ev) { |
62 |
AntModule.err.log("AntModule got ClassLoader change"); |
75 |
AntModule.err.log("AntModule got ModuleInfo change"); |
|
|
76 |
synchronized (this) { |
77 |
if (modules != null) { |
78 |
for (int i = 0; i < modules.length; i++) { |
79 |
modules[i].removePropertyChangeListener(this); |
80 |
} |
81 |
modules = null; |
82 |
} |
83 |
} |
63 |
fireChange(); |
84 |
fireChange(); |
64 |
} |
85 |
} |
|
|
86 |
public synchronized ModuleInfo[] getEnabledModules() { |
87 |
if (modules == null) { |
88 |
Collection c = modulesResult.allInstances(); |
89 |
modules = (ModuleInfo[])c.toArray(new ModuleInfo[c.size()]); |
90 |
for (int i = 0; i < modules.length; i++) { |
91 |
modules[i].addPropertyChangeListener(this); |
92 |
} |
93 |
} |
94 |
List/*<ModuleInfo>*/ enabledModules = new ArrayList(modules.length); |
95 |
for (int i = 0; i < modules.length; i++) { |
96 |
if (modules[i].isEnabled()) { |
97 |
enabledModules.add(modules[i]); |
98 |
} |
99 |
} |
100 |
return (ModuleInfo[])enabledModules.toArray(new ModuleInfo[enabledModules.size()]); |
101 |
} |
65 |
} |
102 |
} |
66 |
private static MiscListener miscListener = new MiscListener(); |
103 |
private static MiscListener miscListener = new MiscListener(); |
67 |
private static Lookup.Result classpathResult = Lookup.getDefault().lookup(new Lookup.Template(ClassLoader.class)); |
104 |
private static Lookup.Result modulesResult = Lookup.getDefault().lookup(new Lookup.Template(ModuleInfo.class)); |
68 |
static { |
105 |
static { |
69 |
AntSettings.getDefault().addPropertyChangeListener(miscListener); |
106 |
AntSettings.getDefault().addPropertyChangeListener(miscListener); |
70 |
classpathResult.addLookupListener(miscListener); |
107 |
modulesResult.addLookupListener(miscListener); |
71 |
} |
108 |
} |
72 |
|
109 |
|
73 |
/** |
110 |
/** |
Lines 106-127
Link Here
|
106 |
} |
143 |
} |
107 |
|
144 |
|
108 |
/** |
145 |
/** |
109 |
* Get the loader which contains the bridge code as well as any |
146 |
* Get the loader which contains the bridge code. |
110 |
* custom-defined tasks. |
|
|
111 |
*/ |
147 |
*/ |
112 |
private static ClassLoader getAuxClassLoader() { |
148 |
private static ClassLoader getBridgeClassLoader() { |
113 |
return (ClassLoader)getStuff().get(KEY_AUX_CLASS_LOADER); |
149 |
return (ClassLoader)getStuff().get(KEY_BRIDGE_CLASS_LOADER); |
114 |
} |
150 |
} |
115 |
|
151 |
|
116 |
/** |
152 |
/** |
117 |
* Get any custom task/type definitions stored in $nbhome/ant/nblib/*.jar. |
153 |
* Get any custom task/type definitions stored in $nbhome/ant/nblib/*.jar. |
118 |
* Some of the classes might not be fully resolvable, so beware. |
154 |
* Some of the classes might not be fully resolvable, so beware. |
|
|
155 |
* The names will include namespace prefixes. |
156 |
* <p> |
157 |
* Only minimal antlib syntax is currently interpreted here: |
158 |
* only <code><taskdef></code> and <code><typedef></code>, |
159 |
* and only the <code>name</code> and <code>classname</code> attributes. |
119 |
*/ |
160 |
*/ |
120 |
public static Map/*<String,Map<String,Class>>*/ getCustomDefs() { |
161 |
public static Map/*<String,Map<String,Class>>*/ getCustomDefsWithNamespace() { |
121 |
return (Map)getStuff().get(KEY_CUSTOM_DEFS); |
162 |
return (Map)getStuff().get(KEY_CUSTOM_DEFS); |
122 |
} |
163 |
} |
123 |
|
164 |
|
124 |
/** |
165 |
/** |
|
|
166 |
* Same as {@link #getCustomDefsWithNamespace} but without any namespace prefixes. |
167 |
*/ |
168 |
public static Map/*<String,Map<String,Class>>*/ getCustomDefsNoNamespace() { |
169 |
Map/*<String,Map<String,Class>>*/ m = new HashMap(); |
170 |
Iterator it = getCustomDefsWithNamespace().entrySet().iterator(); |
171 |
while (it.hasNext()) { |
172 |
Map.Entry entry = (Map.Entry)it.next(); |
173 |
String type = (String)entry.getKey(); |
174 |
Map defs = (Map)entry.getValue(); |
175 |
Map/*Map<String,Class>*/ m2 = new HashMap(); |
176 |
Iterator it2 = defs.entrySet().iterator(); |
177 |
while (it2.hasNext()) { |
178 |
Map.Entry entry2 = (Map.Entry)it2.next(); |
179 |
String fqn = (String)entry2.getKey(); |
180 |
Class clazz = (Class)entry2.getValue(); |
181 |
String name; |
182 |
int idx = fqn.lastIndexOf(':'); |
183 |
if (idx != -1) { |
184 |
name = fqn.substring(idx + 1); |
185 |
} else { |
186 |
name = fqn; |
187 |
} |
188 |
m2.put(name, clazz); |
189 |
} |
190 |
m.put(type, m2); |
191 |
} |
192 |
return m; |
193 |
} |
194 |
|
195 |
/** |
196 |
* Get a map from enabled module code name bases to class loaders containing |
197 |
* JARs from ant/nblib/*.jar. |
198 |
*/ |
199 |
public static Map/*<String,ClassLoader>*/ getCustomDefClassLoaders() throws IOException { |
200 |
return (Map)getStuff().get(KEY_CUSTOM_DEF_CLASS_LOADERS); |
201 |
} |
202 |
|
203 |
/** |
125 |
* Return a class loader which can load from Ant JARs as well the user |
204 |
* Return a class loader which can load from Ant JARs as well the user |
126 |
* development class path. It is not cached, since user classes can |
205 |
* development class path. It is not cached, since user classes can |
127 |
* change quickly. Similar to NbClassLoader. |
206 |
* change quickly. Similar to NbClassLoader. |
Lines 177-188
Link Here
|
177 |
// Ensures that the loader is functional, and that it is at least 1.5.x |
256 |
// Ensures that the loader is functional, and that it is at least 1.5.x |
178 |
// so that our classes can link against it successfully: |
257 |
// so that our classes can link against it successfully: |
179 |
main.loadClass("org.apache.tools.ant.input.InputHandler"); // NOI18N |
258 |
main.loadClass("org.apache.tools.ant.input.InputHandler"); // NOI18N |
180 |
File[] nblibs = getNblibs(); |
259 |
ClassLoader bridgeLoader = createBridgeClassLoader(main); |
181 |
ClassLoader aux = createAuxClassLoader(nblibs, main); |
260 |
m.put(KEY_BRIDGE_CLASS_LOADER, bridgeLoader); |
182 |
m.put(KEY_AUX_CLASS_LOADER, aux); |
261 |
Class impl = bridgeLoader.loadClass("org.apache.tools.ant.module.bridge.impl.BridgeImpl"); // NOI18N |
183 |
Class impl = aux.loadClass("org.apache.tools.ant.module.bridge.impl.BridgeImpl"); // NOI18N |
|
|
184 |
m.put(KEY_BRIDGE, (BridgeInterface)impl.newInstance()); |
262 |
m.put(KEY_BRIDGE, (BridgeInterface)impl.newInstance()); |
185 |
m.put(KEY_CUSTOM_DEFS, createCustomDefs(nblibs, aux)); |
263 |
Map cDCLs = createCustomDefClassLoaders(main); |
|
|
264 |
m.put(KEY_CUSTOM_DEF_CLASS_LOADERS, cDCLs); |
265 |
m.put(KEY_CUSTOM_DEFS, createCustomDefs(cDCLs)); |
186 |
} catch (Exception e) { |
266 |
} catch (Exception e) { |
187 |
fallback(m, e); |
267 |
fallback(m, e); |
188 |
} catch (LinkageError e) { |
268 |
} catch (LinkageError e) { |
Lines 195-206
Link Here
|
195 |
m.clear(); |
275 |
m.clear(); |
196 |
ClassLoader dummy = ClassLoader.getSystemClassLoader(); |
276 |
ClassLoader dummy = ClassLoader.getSystemClassLoader(); |
197 |
m.put(KEY_MAIN_CLASS_LOADER, dummy); |
277 |
m.put(KEY_MAIN_CLASS_LOADER, dummy); |
198 |
m.put(KEY_AUX_CLASS_LOADER, dummy); |
278 |
m.put(KEY_BRIDGE_CLASS_LOADER, dummy); |
199 |
m.put(KEY_BRIDGE, new DummyBridgeImpl(e)); |
279 |
m.put(KEY_BRIDGE, new DummyBridgeImpl(e)); |
200 |
Map defs = new HashMap(); |
280 |
Map defs = new HashMap(); |
201 |
defs.put("task", new HashMap()); // NOI18N |
281 |
defs.put("task", new HashMap()); // NOI18N |
202 |
defs.put("type", new HashMap()); // NOI18N |
282 |
defs.put("type", new HashMap()); // NOI18N |
203 |
m.put(KEY_CUSTOM_DEFS, defs); |
283 |
m.put(KEY_CUSTOM_DEFS, defs); |
|
|
284 |
m.put(KEY_CUSTOM_DEF_CLASS_LOADERS, Collections.EMPTY_MAP); |
204 |
} |
285 |
} |
205 |
|
286 |
|
206 |
private static final class JarFilter implements FilenameFilter { |
287 |
private static final class JarFilter implements FilenameFilter { |
Lines 239-300
Link Here
|
239 |
return new AllPermissionURLClassLoader((URL[])cp.toArray(new URL[cp.size()]), ClassLoader.getSystemClassLoader()); |
320 |
return new AllPermissionURLClassLoader((URL[])cp.toArray(new URL[cp.size()]), ClassLoader.getSystemClassLoader()); |
240 |
} |
321 |
} |
241 |
|
322 |
|
242 |
private static File[] getNblibs() throws IOException { |
323 |
private static ClassLoader createBridgeClassLoader(ClassLoader main) throws Exception { |
243 |
// XXX this will not work for modules installed in the user dir... |
324 |
File bridgeJar = InstalledFileLocator.getDefault().locate("ant/nblib/bridge.jar", "org.apache.tools.ant.module", false); // NOI18N |
244 |
// pending stronger semantics from IFL re. directories |
325 |
if (bridgeJar == null) { |
245 |
// (cf. #36701) |
326 |
throw new IllegalStateException("no ant/nblib/bridge.jar found"); // NOI18N |
246 |
// -> when this is fixed, remove ant/nblib check from org.netbeans.modules.autoupdate.ModuleUpdate |
327 |
} |
247 |
File nblibdir = InstalledFileLocator.getDefault().locate("ant/nblib", "org.apache.tools.ant.module", false); // NOI18N |
328 |
return createAuxClassLoader(bridgeJar, main, AntBridge.class.getClassLoader()); |
248 |
File bridgeJar = new File(nblibdir, "bridge.jar"); |
|
|
249 |
if (!bridgeJar.isFile()) throw new IOException("No such Ant bridge JAR: " + bridgeJar); // NOI18N |
250 |
File[] libs = nblibdir.listFiles(new JarFilter()); |
251 |
if (libs == null) throw new IOException("Listing: " + nblibdir); // NOI18N |
252 |
return libs; |
253 |
} |
329 |
} |
254 |
|
330 |
|
255 |
private static ClassLoader createAuxClassLoader(File[] libs, ClassLoader main) throws Exception { |
331 |
private static ClassLoader createAuxClassLoader(File lib, ClassLoader main, ClassLoader moduleLoader) throws IOException { |
256 |
List cp = new ArrayList(); // List<URL> |
332 |
return new AuxClassLoader(moduleLoader, main, lib.toURI().toURL()); |
257 |
for (int i = 0; i < libs.length; i++) { |
333 |
} |
258 |
cp.add(libs[i].toURI().toURL()); |
334 |
|
|
|
335 |
/** |
336 |
* Get a map from enabled module code name bases to class loaders containing |
337 |
* JARs from ant/nblib/*.jar. |
338 |
*/ |
339 |
private static Map/*<String,ClassLoader>*/ createCustomDefClassLoaders(ClassLoader main) throws IOException { |
340 |
Map/*<String,ClassLoader>*/ m = new HashMap(); |
341 |
ModuleInfo[] modules = miscListener.getEnabledModules(); |
342 |
InstalledFileLocator ifl = InstalledFileLocator.getDefault(); |
343 |
for (int i = 0; i < modules.length; i++) { |
344 |
String cnb = modules[i].getCodeNameBase(); |
345 |
String cnbDashes = cnb.replace('.', '-'); |
346 |
File lib = ifl.locate("ant/nblib/" + cnbDashes + ".jar", cnb, false); // NOI18N |
347 |
if (lib == null) { |
348 |
continue; |
349 |
} |
350 |
ClassLoader l = createAuxClassLoader(lib, main, modules[i].getClassLoader()); |
351 |
m.put(cnb, l); |
259 |
} |
352 |
} |
260 |
ClassLoader nbLoader = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class); |
353 |
return m; |
261 |
return new AuxClassLoader(nbLoader, main, (URL[])cp.toArray(new URL[cp.size()])); |
|
|
262 |
} |
354 |
} |
263 |
|
355 |
|
264 |
private static Map/*<String,Map<String,Class>>*/ createCustomDefs(File[] libs, ClassLoader aux) throws IOException { |
356 |
private static Map/*<String,Map<String,Class>>*/ createCustomDefs(Map cDCLs) throws IOException { |
265 |
Map m = new HashMap(); |
357 |
Map m = new HashMap(); |
266 |
Map tasks = new HashMap(); |
358 |
Map tasks = new HashMap(); |
267 |
Map types = new HashMap(); |
359 |
Map types = new HashMap(); |
|
|
360 |
// XXX #36776: should eventually support <macrodef>s here |
268 |
m.put("task", tasks); // NOI18N |
361 |
m.put("task", tasks); // NOI18N |
269 |
m.put("type", types); // NOI18N |
362 |
m.put("type", types); // NOI18N |
270 |
for (int i = 0; i < libs.length; i++) { |
363 |
Iterator it = cDCLs.entrySet().iterator(); |
271 |
JarFile j = new JarFile(libs[i]); |
364 |
while (it.hasNext()) { |
|
|
365 |
Map.Entry entry = (Map.Entry)it.next(); |
366 |
String cnb = (String)entry.getKey(); |
367 |
ClassLoader l = (ClassLoader)entry.getValue(); |
368 |
String resource = cnb.replace('.', '/') + "/antlib.xml"; // NOI18N |
369 |
URL antlib = l.getResource(resource); |
370 |
if (antlib == null) { |
371 |
throw new IOException("Could not find " + antlib + " in ant/nblib/" + cnb.replace('.', '-') + ".jar"); // NOI18N |
372 |
} |
373 |
Document doc; |
272 |
try { |
374 |
try { |
273 |
JarEntry e = j.getJarEntry("META-INF/taskdefs.properties"); // NOI18N |
375 |
doc = XMLUtil.parse(new InputSource(antlib.toExternalForm()), false, true, /*XXX needed?*/null, null); |
274 |
if (e != null) { |
376 |
} catch (SAXException e) { |
275 |
AntModule.err.log("Loading custom taskdefs from " + libs[i]); |
377 |
throw (IOException)new IOException(e.toString()).initCause(e); |
276 |
loadDefs(j.getInputStream(e), tasks, aux); |
378 |
} |
|
|
379 |
Element docEl = doc.getDocumentElement(); |
380 |
if (!docEl.getLocalName().equals("antlib")) { // NOI18N |
381 |
throw new IOException("Bad root element for " + antlib + ": " + docEl); // NOI18N |
382 |
} |
383 |
NodeList nl = docEl.getChildNodes(); |
384 |
Properties newTaskDefs = new Properties(); |
385 |
Properties newTypeDefs = new Properties(); |
386 |
for (int i = 0; i < nl.getLength(); i++) { |
387 |
Node n = nl.item(i); |
388 |
if (n.getNodeType() != Node.ELEMENT_NODE) { |
389 |
continue; |
277 |
} |
390 |
} |
278 |
e = j.getJarEntry("META-INF/typedefs.properties"); // NOI18N |
391 |
Element def = (Element)n; |
279 |
if (e != null) { |
392 |
boolean type; |
280 |
AntModule.err.log("Loading custom typedefs from " + libs[i]); |
393 |
if (def.getNodeName().equals("taskdef")) { // NOI18N |
281 |
loadDefs(j.getInputStream(e), tasks, aux); |
394 |
type = false; |
|
|
395 |
} else if (def.getNodeName().equals("typedef")) { // NOI18N |
396 |
type = true; |
397 |
} else { |
398 |
AntModule.err.log(ErrorManager.WARNING, "Warning: unrecognized definition " + def + " in " + antlib); |
399 |
continue; |
282 |
} |
400 |
} |
283 |
} finally { |
401 |
String name = def.getAttribute("name"); // NOI18N |
284 |
j.close(); |
402 |
if (name == null) { |
|
|
403 |
// Not a hard error since there might be e.g. <taskdef resource="..."/> here |
404 |
// which we do not parse but which is permitted in antlib by Ant. |
405 |
AntModule.err.log(ErrorManager.WARNING, "Warning: skipping definition " + def + " with no 'name' in " + antlib); |
406 |
continue; |
407 |
} |
408 |
String classname = def.getAttribute("classname"); // NOI18N |
409 |
if (classname == null) { |
410 |
// But this is a hard error. |
411 |
throw new IOException("No 'classname' attr on def of " + name + " in " + antlib); // NOI18N |
412 |
} |
413 |
// XXX would be good to handle at least onerror attr too |
414 |
(type ? newTypeDefs : newTaskDefs).setProperty(name, classname); |
285 |
} |
415 |
} |
|
|
416 |
loadDefs(newTaskDefs, tasks, l); |
417 |
loadDefs(newTypeDefs, types, l); |
286 |
} |
418 |
} |
287 |
return m; |
419 |
return m; |
288 |
} |
420 |
} |
289 |
|
421 |
|
290 |
private static void loadDefs(InputStream is, Map defs, ClassLoader l) throws IOException { |
422 |
private static void loadDefs(Properties p, Map defs, ClassLoader l) throws IOException { |
291 |
// Similar to IntrospectedInfo.load, but just picks up the classes. |
423 |
// Similar to IntrospectedInfo.load, after having parsed the properties. |
292 |
Properties p = new Properties(); |
|
|
293 |
try { |
294 |
p.load(is); |
295 |
} finally { |
296 |
is.close(); |
297 |
} |
298 |
Iterator it = p.entrySet().iterator(); |
424 |
Iterator it = p.entrySet().iterator(); |
299 |
while (it.hasNext()) { |
425 |
while (it.hasNext()) { |
300 |
Map.Entry entry = (Map.Entry)it.next(); |
426 |
Map.Entry entry = (Map.Entry)it.next(); |