KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > system > plugin > PluginInstallerGBean


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.geronimo.system.plugin;
18
19 import java.io.File JavaDoc;
20 import java.io.FileNotFoundException JavaDoc;
21 import java.io.FileOutputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.BufferedOutputStream JavaDoc;
25 import java.net.HttpURLConnection JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.net.URLConnection JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Arrays JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Collections JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.LinkedList JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Set JavaDoc;
40 import java.util.SortedSet JavaDoc;
41 import java.util.Enumeration JavaDoc;
42 import java.util.jar.JarEntry JavaDoc;
43 import java.util.jar.JarFile JavaDoc;
44 import java.util.jar.JarOutputStream JavaDoc;
45 import java.util.jar.Manifest JavaDoc;
46 import java.util.zip.ZipEntry JavaDoc;
47 import javax.security.auth.login.FailedLoginException JavaDoc;
48 import javax.xml.parsers.DocumentBuilder JavaDoc;
49 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
50 import javax.xml.parsers.ParserConfigurationException JavaDoc;
51 import javax.xml.parsers.SAXParser JavaDoc;
52 import javax.xml.parsers.SAXParserFactory JavaDoc;
53 import javax.xml.transform.OutputKeys JavaDoc;
54 import javax.xml.transform.Transformer JavaDoc;
55 import javax.xml.transform.TransformerFactory JavaDoc;
56 import javax.xml.transform.dom.DOMSource JavaDoc;
57 import javax.xml.transform.stream.StreamResult JavaDoc;
58 import org.apache.commons.logging.Log;
59 import org.apache.commons.logging.LogFactory;
60 import org.apache.geronimo.gbean.GBeanInfo;
61 import org.apache.geronimo.gbean.GBeanInfoBuilder;
62 import org.apache.geronimo.kernel.config.ConfigurationData;
63 import org.apache.geronimo.kernel.config.ConfigurationManager;
64 import org.apache.geronimo.kernel.config.ConfigurationStore;
65 import org.apache.geronimo.kernel.config.InvalidConfigException;
66 import org.apache.geronimo.kernel.config.NoSuchConfigException;
67 import org.apache.geronimo.kernel.repository.Artifact;
68 import org.apache.geronimo.kernel.repository.ArtifactResolver;
69 import org.apache.geronimo.kernel.repository.DefaultArtifactResolver;
70 import org.apache.geronimo.kernel.repository.Dependency;
71 import org.apache.geronimo.kernel.repository.FileWriteMonitor;
72 import org.apache.geronimo.kernel.repository.ImportType;
73 import org.apache.geronimo.kernel.repository.MissingDependencyException;
74 import org.apache.geronimo.kernel.repository.Repository;
75 import org.apache.geronimo.kernel.repository.Version;
76 import org.apache.geronimo.kernel.repository.WritableListableRepository;
77 import org.apache.geronimo.kernel.InvalidGBeanException;
78 import org.apache.geronimo.kernel.util.XmlUtil;
79 import org.apache.geronimo.system.configuration.ConfigurationStoreUtil;
80 import org.apache.geronimo.system.configuration.GBeanOverride;
81 import org.apache.geronimo.system.configuration.PluginAttributeStore;
82 import org.apache.geronimo.system.serverinfo.ServerInfo;
83 import org.apache.geronimo.system.threads.ThreadPool;
84 import org.apache.geronimo.util.encoders.Base64;
85 import org.w3c.dom.Document JavaDoc;
86 import org.w3c.dom.Element JavaDoc;
87 import org.w3c.dom.Node JavaDoc;
88 import org.w3c.dom.NodeList JavaDoc;
89 import org.xml.sax.Attributes JavaDoc;
90 import org.xml.sax.ErrorHandler JavaDoc;
91 import org.xml.sax.SAXException JavaDoc;
92 import org.xml.sax.SAXParseException JavaDoc;
93 import org.xml.sax.helpers.DefaultHandler JavaDoc;
94
95 /**
96  * A GBean that knows how to download configurations from a Maven repository.
97  *
98  * @version $Rev: 487158 $ $Date: 2006-12-14 05:12:33 -0500 (Thu, 14 Dec 2006) $
99  */

100 public class PluginInstallerGBean implements PluginInstaller {
101     private final static Log log = LogFactory.getLog(PluginInstallerGBean.class);
102     private static int counter;
103     private ConfigurationManager configManager;
104     private WritableListableRepository writeableRepo;
105     private ConfigurationStore configStore;
106     private ArtifactResolver resolver;
107     private ServerInfo serverInfo;
108     private Map JavaDoc asyncKeys;
109     private ThreadPool threadPool;
110     private PluginAttributeStore attributeStore;
111
112     public PluginInstallerGBean(ConfigurationManager configManager, WritableListableRepository repository, ConfigurationStore configStore, ServerInfo serverInfo, ThreadPool threadPool, PluginAttributeStore store) {
113         this.configManager = configManager;
114         this.writeableRepo = repository;
115         this.configStore = configStore;
116         this.serverInfo = serverInfo;
117         this.threadPool = threadPool;
118         resolver = new DefaultArtifactResolver(null, writeableRepo);
119         asyncKeys = Collections.synchronizedMap(new HashMap JavaDoc());
120         attributeStore = store;
121     }
122
123     /**
124      * Lists the plugins installed in the local Geronimo server, by name and
125      * ID.
126      *
127      * @return A Map with key type String (plugin name) and value type Artifact
128      * (config ID of the plugin).
129      */

130     public Map JavaDoc getInstalledPlugins() {
131         SortedSet JavaDoc artifacts = writeableRepo.list();
132
133         Map JavaDoc plugins = new HashMap JavaDoc();
134         for (Iterator JavaDoc i = artifacts.iterator(); i.hasNext();) {
135             Artifact configId = (Artifact) i.next();
136             File JavaDoc dir = writeableRepo.getLocation(configId);
137             if(dir.isDirectory()) {
138                 File JavaDoc meta = new File JavaDoc(dir, "META-INF");
139                 if(!meta.isDirectory() || !meta.canRead()) {
140                     continue;
141                 }
142                 File JavaDoc xml = new File JavaDoc(meta, "geronimo-plugin.xml");
143                 if(!xml.isFile() || !xml.canRead() || xml.length() == 0) {
144                     continue;
145                 }
146                 readNameAndID(xml, plugins);
147             } else {
148                 if(!dir.isFile() || !dir.canRead()) {
149                     throw new IllegalStateException JavaDoc("Cannot read artifact dir "+dir.getAbsolutePath());
150                 }
151                 try {
152                     JarFile JavaDoc jar = new JarFile JavaDoc(dir);
153                     try {
154                         ZipEntry JavaDoc entry = jar.getEntry("META-INF/geronimo-plugin.xml");
155                         if(entry == null) {
156                             continue;
157                         }
158                         InputStream JavaDoc in = jar.getInputStream(entry);
159                         readNameAndID(in, plugins);
160                         in.close();
161                     } finally {
162                         jar.close();
163                     }
164                 } catch (IOException JavaDoc e) {
165                     log.error("Unable to read JAR file "+dir.getAbsolutePath(), e);
166                 }
167             }
168         }
169         return plugins;
170     }
171
172     /**
173      * Gets a CofigurationMetadata for a configuration installed in the local
174      * server. Should load a saved one if available, or else create a new
175      * default one to the best of its abilities.
176      *
177      * @param moduleId Identifies the configuration. This must match a
178      * configuration currently installed in the local server.
179      * The configId must be fully resolved (isResolved() == true)
180      */

181     public PluginMetadata getPluginMetadata(Artifact moduleId) {
182         if(configManager != null) {
183             if(!configManager.isConfiguration(moduleId)) {
184                 return null;
185             }
186         } else {
187             if(!configStore.containsConfiguration(moduleId)) {
188                 return null;
189             }
190         }
191         File JavaDoc dir = writeableRepo.getLocation(moduleId);
192         Document JavaDoc doc;
193         ConfigurationData configData;
194         String JavaDoc source = dir.getAbsolutePath();
195         try {
196             if(dir.isDirectory()) {
197                 File JavaDoc meta = new File JavaDoc(dir, "META-INF");
198                 if(!meta.isDirectory() || !meta.canRead()) {
199                     return null;
200                 }
201                 File JavaDoc xml = new File JavaDoc(meta, "geronimo-plugin.xml");
202                 configData = configStore.loadConfiguration(moduleId);
203                 if(!xml.isFile() || !xml.canRead() || xml.length() == 0) {
204                     return createDefaultMetadata(configData);
205                 }
206                 source = xml.getAbsolutePath();
207                 DocumentBuilder JavaDoc builder = createDocumentBuilder();
208                 doc = builder.parse(xml);
209             } else {
210                 if(!dir.isFile() || !dir.canRead()) {
211                     throw new IllegalStateException JavaDoc("Cannot read configuration "+dir.getAbsolutePath());
212                 }
213                 configData = configStore.loadConfiguration(moduleId);
214                 JarFile JavaDoc jar = new JarFile JavaDoc(dir);
215                 try {
216                     ZipEntry JavaDoc entry = jar.getEntry("META-INF/geronimo-plugin.xml");
217                     if(entry == null) {
218                         return createDefaultMetadata(configData);
219                     }
220                     source = dir.getAbsolutePath()+"#META-INF/geronimo-plugin.xml";
221                     InputStream JavaDoc in = jar.getInputStream(entry);
222                     DocumentBuilder JavaDoc builder = createDocumentBuilder();
223                     doc = builder.parse(in);
224                     in.close();
225                 } finally {
226                     jar.close();
227                 }
228             }
229             PluginMetadata result = loadPluginMetadata(doc, source);
230             overrideDependencies(configData, result);
231             return result;
232         } catch (InvalidConfigException e) {
233             e.printStackTrace();
234             log.warn("Unable to generate metadata for "+moduleId, e);
235         } catch (Exception JavaDoc e) {
236             e.printStackTrace();
237             log.warn("Invalid XML at "+source, e);
238         }
239         return null;
240     }
241
242     /**
243      * Saves a ConfigurationMetadata for a particular plugin, if the server is
244      * able to record it. This can be used if you later re-export the plugin,
245      * or just want to review the information for a particular installed
246      * plugin.
247      *
248      * @param metadata The data to save. The contained configId (which must
249      * be fully resolved) identifies the configuration to save
250      * this for.
251      */

252     public void updatePluginMetadata(PluginMetadata metadata) {
253         File JavaDoc dir = writeableRepo.getLocation(metadata.getModuleId());
254         if(dir == null) {
255             throw new IllegalArgumentException JavaDoc(metadata.getModuleId()+" is not installed!");
256         }
257         if(!dir.isDirectory()) { // must be a packed (JAR-formatted) plugin
258
try {
259                 File JavaDoc temp = new File JavaDoc(dir.getParentFile(), dir.getName()+".temp");
260                 JarFile JavaDoc input = new JarFile JavaDoc(dir);
261                 Manifest JavaDoc manifest = input.getManifest();
262                 JarOutputStream JavaDoc out = manifest == null ? new JarOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(temp)))
263                         : new JarOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(temp)), manifest);
264                 Enumeration JavaDoc en = input.entries();
265                 byte[] buf = new byte[4096];
266                 int count;
267                 while (en.hasMoreElements()) {
268                     JarEntry JavaDoc entry = (JarEntry JavaDoc) en.nextElement();
269                     if(entry.getName().equals("META-INF/geronimo-plugin.xml")) {
270                         entry = new JarEntry JavaDoc(entry.getName());
271                         out.putNextEntry(entry);
272                         Document JavaDoc doc = writePluginMetadata(metadata);
273                         TransformerFactory JavaDoc xfactory = XmlUtil.newTransformerFactory();
274                         Transformer JavaDoc xform = xfactory.newTransformer();
275                         xform.setOutputProperty(OutputKeys.INDENT, "yes");
276                         xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
277                         xform.transform(new DOMSource JavaDoc(doc), new StreamResult JavaDoc(out));
278                     } else if(entry.getName().equals("META-INF/MANIFEST.MF")) {
279                         // do nothing, already passed in a manifest
280
} else {
281                         out.putNextEntry(entry);
282                         InputStream JavaDoc in = input.getInputStream(entry);
283                         while((count = in.read(buf)) > -1) {
284                             out.write(buf, 0, count);
285                         }
286                         in.close();
287                         out.closeEntry();
288                     }
289                 }
290                 out.flush();
291                 out.close();
292                 input.close();
293                 if(!dir.delete()) {
294                     throw new IOException JavaDoc("Unable to delete old plugin at "+dir.getAbsolutePath());
295                 }
296                 if(!temp.renameTo(dir)) {
297                     throw new IOException JavaDoc("Unable to move new plugin "+temp.getAbsolutePath()+" to "+dir.getAbsolutePath());
298                 }
299             } catch (Exception JavaDoc e) {
300                 log.error("Unable to update plugin metadata", e);
301                 throw new RuntimeException JavaDoc("Unable to update plugin metadata", e);
302             } // TODO this really should have a finally block to ensure streams are closed
303
} else {
304             File JavaDoc meta = new File JavaDoc(dir, "META-INF");
305             if(!meta.isDirectory() || !meta.canRead()) {
306                 throw new IllegalArgumentException JavaDoc(metadata.getModuleId()+" is not a plugin!");
307             }
308             File JavaDoc xml = new File JavaDoc(meta, "geronimo-plugin.xml");
309             FileOutputStream JavaDoc fos = null;
310             try {
311                 if(!xml.isFile()) {
312                     if(!xml.createNewFile()) {
313                         throw new RuntimeException JavaDoc("Cannot create plugin metadata file for "+metadata.getModuleId());
314                     }
315                 }
316                 Document JavaDoc doc = writePluginMetadata(metadata);
317                 TransformerFactory JavaDoc xfactory = XmlUtil.newTransformerFactory();
318                 Transformer JavaDoc xform = xfactory.newTransformer();
319                 xform.setOutputProperty(OutputKeys.INDENT, "yes");
320                 xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
321                 fos = new FileOutputStream JavaDoc(xml);
322                 // use a FileOutputStream instead of a File on the StreamResult
323
// constructor as problems were encountered with the file not being closed.
324
StreamResult JavaDoc sr = new StreamResult JavaDoc(fos);
325                 xform.transform(new DOMSource JavaDoc(doc), sr);
326             } catch (Exception JavaDoc e) {
327                 log.error("Unable to save plugin metadata for "+metadata.getModuleId(), e);
328             } finally {
329                 if (fos != null) {
330                     try {
331                         fos.close();
332                     } catch (IOException JavaDoc ignored) {
333                         // ignored
334
}
335                 }
336             }
337         }
338     }
339
340     /**
341      * Lists the plugins available for download in a particular Geronimo repository.
342      *
343      * @param mavenRepository The base URL to the maven repository. This must
344      * contain the file geronimo-plugins.xml
345      * @param username Optional username, if the maven repo uses HTTP Basic authentication.
346      * Set this to null if no authentication is required.
347      * @param password Optional password, if the maven repo uses HTTP Basic authentication.
348      * Set this to null if no authentication is required.
349      */

350     public PluginList listPlugins(URL JavaDoc mavenRepository, String JavaDoc username, String JavaDoc password) throws IOException JavaDoc, FailedLoginException JavaDoc {
351         String JavaDoc repository = mavenRepository.toString();
352         if(!repository.endsWith("/")) {
353             repository = repository+"/";
354         }
355         //todo: Try downloading a .gz first
356
URL JavaDoc url = new URL JavaDoc(repository+"geronimo-plugins.xml");
357         try {
358             //todo: use a progress monitor
359
InputStream JavaDoc in = openStream(null, new URL JavaDoc[]{url}, username, password, null).getStream();
360             return loadPluginList(mavenRepository, in);
361         } catch (MissingDependencyException e) {
362             log.error("Cannot find plugin index at site "+url);
363             return null;
364         } catch (Exception JavaDoc e) {
365             log.error("Unable to load repository configuration data", e);
366             return null;
367         }
368     }
369
370     /**
371      * Installs a configuration from a remote repository into the local Geronimo server,
372      * including all its dependencies. The caller will get the results when the
373      * operation completes. Note that this method does not throw exceptions on failure,
374      * but instead sets the failure property of the DownloadResults.
375      *
376      * @param username Optional username, if the maven repo uses HTTP Basic authentication.
377      * Set this to null if no authentication is required.
378      * @param password Optional password, if the maven repo uses HTTP Basic authentication.
379      * Set this to null if no authentication is required.
380      * @param pluginsToInstall The list of configurations to install
381      */

382     public DownloadResults install(PluginList pluginsToInstall, String JavaDoc username, String JavaDoc password) {
383         DownloadResults results = new DownloadResults();
384         install(pluginsToInstall, username, password, results);
385         return results;
386     }
387
388     /**
389      * Installs a configuration from a remote repository into the local Geronimo server,
390      * including all its dependencies. The method blocks until the operation completes,
391      * but the caller will be notified of progress frequently along the way (using the
392      * supplied DownloadPoller). Therefore the caller is meant to create the poller and
393      * then call this method in a background thread. Note that this method does not
394      * throw exceptions on failure, but instead sets the failure property of the
395      * DownloadPoller.
396      *
397      * @param pluginsToInstall The list of configurations to install
398      * @param username Optional username, if the maven repo uses HTTP Basic authentication.
399      * Set this to null if no authentication is required.
400      * @param password Optional password, if the maven repo uses HTTP Basic authentication.
401      * Set this to null if no authentication is required.
402      * @param poller Will be notified with status updates as the download proceeds
403      */

404     public void install(PluginList pluginsToInstall, String JavaDoc username, String JavaDoc password, DownloadPoller poller) {
405         try {
406             Map JavaDoc metaMap = new HashMap JavaDoc();
407             // Step 1: validate everything
408
for (int i = 0; i < pluginsToInstall.getPlugins().length; i++) {
409                 PluginMetadata metadata = pluginsToInstall.getPlugins()[i];
410                 validatePlugin(metadata);
411                 if(metadata.getModuleId() != null) {
412                     metaMap.put(metadata.getModuleId(), metadata);
413                 }
414             }
415
416             // Step 2: everything is valid, do the installation
417
for (int i = 0; i < pluginsToInstall.getPlugins().length; i++) {
418                 // 1. Identify the configuration
419
PluginMetadata metadata = pluginsToInstall.getPlugins()[i];
420                 // 2. Unload obsoleted configurations
421
List JavaDoc obsoletes = new ArrayList JavaDoc();
422                 for (int j = 0; j < metadata.getObsoletes().length; j++) {
423                     String JavaDoc name = metadata.getObsoletes()[j];
424                     Artifact obsolete = Artifact.create(name);
425                     Artifact[] list = configManager.getArtifactResolver().queryArtifacts(obsolete);
426                     for (int k = 0; k < list.length; k++) {
427                         Artifact artifact = list[k];
428                         if(configManager.isLoaded(artifact)) {
429                             if(configManager.isRunning(artifact)) {
430                                 configManager.stopConfiguration(artifact);
431                             }
432                             configManager.unloadConfiguration(artifact);
433                             obsoletes.add(artifact);
434                         }
435                     }
436                 }
437                 // 3. Download the artifact if necessary, and its dependencies
438
Set JavaDoc working = new HashSet JavaDoc();
439                 if(metadata.getModuleId() != null) {
440                     URL JavaDoc[] repos = pluginsToInstall.getRepositories();
441                     if(metadata.getRepositories().length > 0) {
442                         repos = metadata.getRepositories();
443                     }
444                     downloadArtifact(metadata.getModuleId(), metaMap, repos,
445                             username, password, new ResultsFileWriteMonitor(poller), working, false);
446                 } else {
447                     String JavaDoc[] deps = metadata.getDependencies();
448                     for (int j = 0; j < deps.length; j++) {
449                         String JavaDoc dep = deps[j];
450                         Artifact entry = Artifact.create(dep);
451                         URL JavaDoc[] repos = pluginsToInstall.getRepositories();
452                         if(metadata.getRepositories().length > 0) {
453                             repos = metadata.getRepositories();
454                         }
455                         downloadArtifact(entry, metaMap, repos,
456                                 username, password, new ResultsFileWriteMonitor(poller), working, false);
457                     }
458                 }
459                 // 4. Uninstall obsolete configurations
460
for (int j = 0; j < obsoletes.size(); j++) {
461                     Artifact artifact = (Artifact) obsoletes.get(j);
462                     configManager.uninstallConfiguration(artifact);
463                 }
464                 // 5. Installation of this configuration finished successfully
465
}
466
467             // Step 3: Start anything that's marked accordingly
468
for (int i = 0; i < pluginsToInstall.getPlugins().length; i++) {
469                 PluginMetadata metadata = pluginsToInstall.getPlugins()[i];
470                 for (int j = 0; j < metadata.getForceStart().length; j++) {
471                     String JavaDoc id = metadata.getForceStart()[j];
472                     Artifact artifact = Artifact.create(id);
473                     if(configManager.isConfiguration(artifact)) {
474                         poller.setCurrentFilePercent(-1);
475                         poller.setCurrentMessage("Starting "+artifact);
476                         configManager.loadConfiguration(artifact);
477                         configManager.startConfiguration(artifact);
478                     }
479                 }
480             }
481         } catch (Exception JavaDoc e) {
482             poller.setFailure(e);
483         } finally {
484             poller.setFinished();
485         }
486     }
487
488     /**
489      * Installs a configuration from a remote repository into the local Geronimo server,
490      * including all its dependencies. The method returns immediately, providing a key
491      * that can be used to poll the status of the download operation. Note that the
492      * installation does not throw exceptions on failure, but instead sets the failure
493      * property of the DownloadResults that the caller can poll for.
494      *
495      * @param pluginsToInstall The list of configurations to install
496      * @param username Optional username, if the maven repo uses HTTP Basic authentication.
497      * Set this to null if no authentication is required.
498      * @param password Optional password, if the maven repo uses HTTP Basic authentication.
499      * Set this to null if no authentication is required.
500      *
501      * @return A key that can be passed to checkOnInstall
502      */

503     public Object JavaDoc startInstall(final PluginList pluginsToInstall, final String JavaDoc username, final String JavaDoc password) {
504         Object JavaDoc key = getNextKey();
505         final DownloadResults results = new DownloadResults();
506         Runnable JavaDoc work = new Runnable JavaDoc() {
507             public void run() {
508                 install(pluginsToInstall, username, password, results);
509             }
510         };
511         asyncKeys.put(key, results);
512         try {
513             threadPool.execute("Configuration Installer", work);
514         } catch (InterruptedException JavaDoc e) {
515             throw new RuntimeException JavaDoc("Unable to start work", e);
516         }
517         return key;
518     }
519
520     /**
521      * Installs a configuration downloaded from a remote repository into the local Geronimo
522      * server, including all its dependencies. The method returns immediately, providing a
523      * key that can be used to poll the status of the download operation. Note that the
524      * installation does not throw exceptions on failure, but instead sets the failure
525      * property of the DownloadResults that the caller can poll for.
526      *
527      * @param carFile A CAR file downloaded from a remote repository. This is a packaged
528      * configuration with included configuration information, but it may
529      * still have external dependencies that need to be downloaded
530      * separately. The metadata in the CAR file includes a repository URL
531      * for these downloads, and the username and password arguments are
532      * used in conjunction with that.
533      * @param username Optional username, if the maven repo uses HTTP Basic authentication.
534      * Set this to null if no authentication is required.
535      * @param password Optional password, if the maven repo uses HTTP Basic authentication.
536      * Set this to null if no authentication is required.
537      *
538      * @return A key that can be passed to checkOnInstall
539      */

540     public Object JavaDoc startInstall(final File JavaDoc carFile, final String JavaDoc username, final String JavaDoc password) {
541         Object JavaDoc key = getNextKey();
542         final DownloadResults results = new DownloadResults();
543         Runnable JavaDoc work = new Runnable JavaDoc() {
544             public void run() {
545                 install(carFile, username, password, results);
546             }
547         };
548         asyncKeys.put(key, results);
549         try {
550             threadPool.execute("Configuration Installer", work);
551         } catch (InterruptedException JavaDoc e) {
552             throw new RuntimeException JavaDoc("Unable to start work", e);
553         }
554         return key;
555     }
556
557     /**
558      * Gets the current progress of a download operation. Note that once the
559      * DownloadResults is returned for this operation shows isFinished = true,
560      * the operation will be forgotten, so the caller should be careful not to
561      * call this again after the download has finished.
562      *
563      * @param key Identifies the operation to check on
564      */

565     public DownloadResults checkOnInstall(Object JavaDoc key) {
566         DownloadResults results = (DownloadResults) asyncKeys.get(key);
567         results = results.duplicate();
568         if(results.isFinished()) {
569             asyncKeys.remove(key);
570         }
571         return results;
572     }
573
574     /**
575      * Installs from a pre-downloaded CAR file
576      */

577     public void install(File JavaDoc carFile, String JavaDoc username, String JavaDoc password, DownloadPoller poller) {
578         try {
579             // 1. Extract the configuration metadata
580
PluginMetadata data = loadCARFile(carFile, true);
581             if(data == null) {
582                 throw new IllegalArgumentException JavaDoc("Invalid Configuration Archive "+carFile.getAbsolutePath()+" see server log for details");
583             }
584
585             // 2. Validate that we can install this
586
validatePlugin(data);
587
588             // 3. Install the CAR into the repository (it shouldn't be re-downloaded)
589
if(data.getModuleId() != null) {
590                 ResultsFileWriteMonitor monitor = new ResultsFileWriteMonitor(poller);
591                 writeableRepo.copyToRepository(carFile, data.getModuleId(), monitor);
592                 installConfigXMLData(data.getModuleId(), data);
593                 if(data.getFilesToCopy() != null) {
594                     extractPluginFiles(data.getModuleId(), data, monitor);
595                 }
596             }
597
598             // 4. Use the standard logic to remove obsoletes, install dependencies, etc.
599
// This will validate all over again (oh, well)
600
install(new PluginList(data.getRepositories(), new PluginMetadata[]{data}),
601                     username, password, poller);
602         } catch (Exception JavaDoc e) {
603             poller.setFailure(e);
604         } finally {
605             poller.setFinished();
606         }
607     }
608
609     /**
610      * Ensures that a plugin is installable.
611      */

612     private void validatePlugin(PluginMetadata metadata) throws MissingDependencyException {
613         // 1. Check that it's not already running
614
if(metadata.getModuleId() != null) { // that is, it's a real configuration not a plugin list
615
if(configManager.isRunning(metadata.getModuleId())) {
616                 boolean upgrade = false;
617                 for (int i = 0; i < metadata.getObsoletes().length; i++) {
618                     String JavaDoc obsolete = metadata.getObsoletes()[i];
619                     Artifact test = Artifact.create(obsolete);
620                     if(test.matches(metadata.getModuleId())) {
621                         upgrade = true;
622                         break;
623                     }
624                 }
625                 if(!upgrade) {
626                     throw new IllegalArgumentException JavaDoc("Configuration "+metadata.getModuleId()+" is already running!");
627                 }
628             }
629         }
630         // 2. Check that we meet the prerequisites
631
PluginMetadata.Prerequisite[] prereqs = metadata.getPrerequisites();
632         for (int i = 0; i < prereqs.length; i++) {
633             PluginMetadata.Prerequisite prereq = prereqs[i];
634             if(resolver.queryArtifacts(prereq.getModuleId()).length == 0) {
635                 throw new MissingDependencyException("Required configuration '"+prereq.getModuleId()+"' is not installed.");
636             }
637         }
638         // 3. Check that we meet the Geronimo, JVM versions
639
if(metadata.getGeronimoVersions().length > 0 && !checkGeronimoVersions(metadata.getGeronimoVersions())) {
640             throw new MissingDependencyException("Cannot install plugin "+metadata.getModuleId()+" on Geronimo "+serverInfo.getVersion());
641         }
642         if(metadata.getJvmVersions().length > 0 && !checkJVMVersions(metadata.getJvmVersions())) {
643             throw new MissingDependencyException("Cannot install plugin "+metadata.getModuleId()+" on JVM "+System.getProperty("java.version"));
644         }
645     }
646
647     /**
648      * Download (if necessary) and install something, which may be a Configuration or may
649      * be just a JAR. For each artifact processed, all its dependencies will be
650      * processed as well.
651      *
652      * @param configID Identifies the artifact to install
653      * @param repos The URLs to contact the repositories (in order of preference)
654      * @param username The username used for repositories secured with HTTP Basic authentication
655      * @param password The password used for repositories secured with HTTP Basic authentication
656      * @param monitor The ongoing results of the download operations, with some monitoring logic
657      *
658      * @throws IOException When there's a problem reading or writing data
659      * @throws FailedLoginException When a repository requires authentication and either no username
660      * and password are supplied or the username and password supplied
661      * are not accepted
662      * @throws MissingDependencyException When a dependency cannot be located in any of the listed repositories
663      */

664     private void downloadArtifact(Artifact configID, Map JavaDoc metadata, URL JavaDoc[] repos, String JavaDoc username, String JavaDoc password, ResultsFileWriteMonitor monitor, Set JavaDoc soFar, boolean dependency) throws IOException JavaDoc, FailedLoginException JavaDoc, MissingDependencyException {
665         if(soFar.contains(configID)) {
666             return; // Avoid enless work due to circular dependencies
667
} else {
668             soFar.add(configID);
669         }
670         // Download and install the main artifact
671
boolean pluginWasInstalled = false;
672         Artifact[] matches = configManager.getArtifactResolver().queryArtifacts(configID);
673         if(matches.length == 0) { // not present, needs to be downloaded
674
monitor.getResults().setCurrentMessage("Downloading " + configID);
675             monitor.getResults().setCurrentFilePercent(-1);
676             OpenResult result = openStream(configID, repos, username, password, monitor);
677             try {
678                 File JavaDoc tempFile = downloadFile(result, monitor);
679                 if (tempFile == null) {
680                     log.error("Null filehandle was returned for "+configID);
681                     throw new IllegalArgumentException JavaDoc("Null filehandle was returned for "+configID);
682                 }
683                 PluginMetadata pluginData = ((PluginMetadata) metadata.get(configID));
684                 // Only bother with the hash if we got it from a source other than the download file itself
685
PluginMetadata.Hash hash = pluginData == null ? null : pluginData.getHash();
686                 if(hash != null) {
687                     String JavaDoc actual = ConfigurationStoreUtil.getActualChecksum(tempFile, hash.getType());
688                     if(!actual.equals(hash.getValue())) {
689                         throw new IOException JavaDoc("File download incorrect (expected "+hash.getType()+" hash "+hash.getValue()+" but got "+actual+")");
690                     }
691                 }
692                 // See if the download file has plugin metadata
693
if(pluginData == null) {
694                     try {
695                         pluginData = loadCARFile(tempFile, false);
696                     } catch (Exception JavaDoc e) {
697                         throw new IOException JavaDoc("Unable to read plugin metadata: "+e.getMessage());
698                     }
699                 }
700                 if(pluginData != null) { // it's a plugin, not a plain JAR
701
validatePlugin(pluginData);
702                 }
703                 monitor.getResults().setCurrentMessage("Copying " + result.getConfigID() + " to the repository");
704                 writeableRepo.copyToRepository(tempFile, result.getConfigID(), monitor); //todo: download SNAPSHOTS if previously available?
705
if(!tempFile.delete()) {
706                     log.warn("Unable to delete temporary download file "+tempFile.getAbsolutePath());
707                     tempFile.deleteOnExit();
708                 }
709                 if (pluginData != null)
710                     installConfigXMLData(result.getConfigID(), pluginData);
711                 else
712                     log.debug("No config XML data to install.");
713                 if(dependency) {
714                     monitor.getResults().addDependencyInstalled(configID);
715                     configID = result.getConfigID();
716                 } else {
717                     configID = result.getConfigID();
718                     monitor.getResults().addInstalledConfigID(configID);
719                 }
720                 pluginWasInstalled = true;
721             } finally {
722                 result.getStream().close();
723             }
724         } else {
725             if(dependency) {
726                 monitor.getResults().addDependencyPresent(configID);
727             } else {
728                 monitor.getResults().addInstalledConfigID(configID);
729             }
730         }
731         // Download and install the dependencies
732
try {
733             ConfigurationData data = null;
734             if(!configID.isResolved()) {
735                 // See if something's running
736
for (int i = matches.length-1; i >= 0; i--) {
737                     Artifact match = matches[i];
738                     if(configStore.containsConfiguration(match) && configManager.isRunning(match)) {
739                         return; // its dependencies must be OK
740
}
741                 }
742                 // Go with something that's installed
743
configID = matches[matches.length-1];
744             }
745             if(configStore.containsConfiguration(configID)) {
746                 if(configManager.isRunning(configID)) {
747                     return; // its dependencies must be OK
748
}
749                 data = configStore.loadConfiguration(configID);
750             }
751             Dependency[] dependencies = data == null ? getDependencies(writeableRepo, configID) : getDependencies(data);
752             // Download the dependencies
753
for (int i = 0; i < dependencies.length; i++) {
754                 Dependency dep = dependencies[i];
755                 Artifact artifact = dep.getArtifact();
756                 downloadArtifact(artifact, metadata, repos, username, password, monitor, soFar, true);
757             }
758         } catch (NoSuchConfigException e) {
759             throw new IllegalStateException JavaDoc("Installed configuration into repository but ConfigStore does not see it: "+e.getMessage());
760         } catch (InvalidConfigException e) {
761             throw new IllegalStateException JavaDoc("Installed configuration into repository but ConfigStore cannot load it: "+e.getMessage());
762         }
763         // Copy any files out of the artifact
764
PluginMetadata currentPlugin = configManager.isConfiguration(configID) ? getPluginMetadata(configID) : null;
765         if(pluginWasInstalled && currentPlugin != null && currentPlugin.getFilesToCopy() != null) {
766             extractPluginFiles(configID, currentPlugin, monitor);
767         }
768     }
769
770     private void extractPluginFiles(Artifact configID, PluginMetadata currentPlugin, ResultsFileWriteMonitor monitor) throws IOException JavaDoc {
771         for (int i = 0; i < currentPlugin.getFilesToCopy().length; i++) {
772             PluginMetadata.CopyFile data = currentPlugin.getFilesToCopy()[i];
773             monitor.getResults().setCurrentFilePercent(-1);
774             monitor.getResults().setCurrentFile(data.getSourceFile());
775             monitor.getResults().setCurrentMessage("Copying "+data.getSourceFile()+" from plugin to Geronimo installation");
776             Set JavaDoc set;
777             try {
778                 set = configStore.resolve(configID, null, data.getSourceFile());
779             } catch (NoSuchConfigException e) {
780                 throw new IllegalStateException JavaDoc("Unable to identify module "+configID+" to copy files from");
781             }
782             if(set.size() == 0) {
783                 log.error("Installed configuration into repository but cannot locate file to copy "+data.getSourceFile());
784                 continue;
785             }
786             File JavaDoc targetDir = data.isRelativeToVar() ? serverInfo.resolveServer("var/"+data.getDestDir()) : serverInfo.resolve(data.getDestDir());
787             if(!targetDir.isDirectory()) {
788                 log.error("Plugin install cannot write file "+data.getSourceFile()+" to "+data.getDestDir()+" because "+targetDir.getAbsolutePath()+" is not a directory");
789                 continue;
790             }
791             if(!targetDir.canWrite()) {
792                 log.error("Plugin install cannot write file "+data.getSourceFile()+" to "+data.getDestDir()+" because "+targetDir.getAbsolutePath()+" is not writable");
793                 continue;
794             }
795             for (Iterator JavaDoc it = set.iterator(); it.hasNext();) {
796                 URL JavaDoc url = (URL JavaDoc) it.next();
797                 String JavaDoc path = url.getPath();
798                 if(path.lastIndexOf('/') > -1) {
799                     path = path.substring(path.lastIndexOf('/'));
800                 }
801                 File JavaDoc target = new File JavaDoc(targetDir, path);
802                 if(!target.exists()) {
803                     if(!target.createNewFile()) {
804                         log.error("Plugin install cannot create new file "+target.getAbsolutePath());
805                         continue;
806                     }
807                 }
808                 if(!target.canWrite()) {
809                     log.error("Plugin install cannot write to file "+target.getAbsolutePath());
810                     continue;
811                 }
812                 copyFile(url.openStream(), new FileOutputStream JavaDoc(target));
813             }
814         }
815     }
816
817     private void copyFile(InputStream JavaDoc in, FileOutputStream JavaDoc out) throws IOException JavaDoc {
818         byte[] buf = new byte[4096];
819         int count;
820         while((count = in.read(buf)) > -1) {
821             out.write(buf, 0, count);
822         }
823         in.close();
824         out.flush();
825         out.close();
826     }
827
828     /**
829      * Downloads to a temporary file so we can validate the download before
830      * installing into the repository.
831      */

832     private File JavaDoc downloadFile(OpenResult result, ResultsFileWriteMonitor monitor) throws IOException JavaDoc {
833         InputStream JavaDoc in = result.getStream();
834         if(in == null) {
835             throw new IllegalStateException JavaDoc();
836         }
837         FileOutputStream JavaDoc out = null;
838         try {
839             monitor.writeStarted(result.getConfigID().toString(), result.fileSize);
840             File JavaDoc file = File.createTempFile("geronimo-plugin-download-", ".tmp");
841             out = new FileOutputStream JavaDoc(file);
842             byte[] buf = new byte[4096];
843             int count, total = 0;
844             while((count = in.read(buf)) > -1) {
845                 out.write(buf, 0, count);
846                 monitor.writeProgress(total += count);
847             }
848             monitor.writeComplete(total);
849             in.close();
850             in = null;
851             out.close();
852             out = null;
853             return file;
854         } finally {
855             if (in != null) {
856                 try {
857                     in.close();
858                 } catch (IOException JavaDoc ignored) { }
859             }
860             if (out != null) {
861                 try {
862                     out.close();
863                 } catch (IOException JavaDoc ignored) { }
864             }
865         }
866     }
867
868     /**
869      * Used to get dependencies for a JAR
870      */

871     private static Dependency[] getDependencies(Repository repo, Artifact artifact) {
872         Set JavaDoc set = repo.getDependencies(artifact);
873         Dependency[] results = new Dependency[set.size()];
874         int index=0;
875         for (Iterator JavaDoc it = set.iterator(); it.hasNext(); ++index) {
876             Artifact dep = (Artifact) it.next();
877             results[index] = new Dependency(dep, ImportType.CLASSES);
878         }
879         return results;
880     }
881
882     /**
883      * Used to get dependencies for a Configuration
884      */

885     private static Dependency[] getDependencies(ConfigurationData data) {
886         List JavaDoc dependencies = new ArrayList JavaDoc(data.getEnvironment().getDependencies());
887         Collection JavaDoc children = data.getChildConfigurations().values();
888         for (Iterator JavaDoc it = children.iterator(); it.hasNext();) {
889             ConfigurationData child = (ConfigurationData) it.next();
890             dependencies.addAll(child.getEnvironment().getDependencies());
891         }
892         return (Dependency[]) dependencies.toArray(new Dependency[dependencies.size()]);
893     }
894
895     /**
896      * Constructs a URL to a particular artifact in a particular repository
897      */

898     private static URL JavaDoc getURL(Artifact configId, URL JavaDoc repository) throws MalformedURLException JavaDoc {
899     URL JavaDoc context;
900     if(repository.toString().endsWith("/")) {
901         context = repository;
902     } else {
903         context = new URL JavaDoc(repository.toString()+"/");
904     }
905
906     String JavaDoc qualifiedVersion = configId.getVersion().toString();
907     if (configId.getVersion() instanceof SnapshotVersion) {
908             SnapshotVersion ssVersion = (SnapshotVersion)configId.getVersion();
909             String JavaDoc timestamp = ssVersion.getTimestamp();
910             int buildNumber = ssVersion.getBuildNumber();
911             if (timestamp!=null && buildNumber!=0) {
912                 qualifiedVersion = qualifiedVersion.replaceAll("SNAPSHOT", timestamp + "-" + buildNumber);
913             }
914     }
915         return new URL JavaDoc(context, configId.getGroupId().replace('.','/') + "/"
916                      + configId.getArtifactId() + "/" + configId.getVersion()
917                      + "/" +configId.getArtifactId() + "-"
918                      + qualifiedVersion + "." +configId.getType());
919     }
920
921     /**
922      * Attemps to open a stream to an artifact in one of the listed repositories.
923      * The username and password provided are only used if one of the repositories
924      * returns an HTTP authentication failure on the first try.
925      *
926      * @param artifact The artifact we're looking for, or null to just connect to the base repo URL
927      * @param repos The base URLs to the repositories to search for the artifact
928      * @param username A username if one of the repositories might require authentication
929      * @param password A password if one of the repositories might require authentication
930      * @param monitor Callback for progress on the connection operation
931      *
932      * @throws IOException Occurs when the IO with the repository failed
933      * @throws FailedLoginException Occurs when a repository requires authentication and either
934      * no username and password were provided or they weren't
935      * accepted
936      * @throws MissingDependencyException Occurs when none of the repositories has the artifact
937      * in question
938      */

939     private static OpenResult openStream(Artifact artifact, URL JavaDoc[] repos, String JavaDoc username, String JavaDoc password, ResultsFileWriteMonitor monitor) throws IOException JavaDoc, FailedLoginException JavaDoc, MissingDependencyException {
940         if(artifact != null) {
941             if (!artifact.isResolved() || artifact.getVersion().toString().indexOf("SNAPSHOT") >= 0) {
942                 artifact = findArtifact(artifact, repos, username, password, monitor);
943             }
944         }
945         if(monitor != null) {
946             monitor.getResults().setCurrentFilePercent(-1);
947             monitor.getResults().setCurrentMessage("Downloading "+artifact+"...");
948             monitor.setTotalBytes(-1); // In case the server doesn't say
949
}
950         InputStream JavaDoc in;
951         LinkedList JavaDoc list = new LinkedList JavaDoc();
952         list.addAll(Arrays.asList(repos));
953         while (true) {
954             if(list.isEmpty()) {
955                 throw new MissingDependencyException("Unable to download dependency "+artifact);
956             }
957             if(monitor != null) {
958                 monitor.setTotalBytes(-1); // Just to be sure
959
}
960             URL JavaDoc repository = (URL JavaDoc) list.removeFirst();
961             URL JavaDoc url = artifact == null ? repository : getURL(artifact, repository);
962             log.debug("Attempting to download "+artifact+" from "+url);
963             in = connect(url, username, password, monitor);
964             if(in != null) {
965                 return new OpenResult(artifact, in, monitor == null ? -1 : monitor.getTotalBytes());
966             }
967         }
968     }
969
970     /**
971      * Does the meat of connecting to a URL
972      */

973     private static InputStream JavaDoc connect(URL JavaDoc url, String JavaDoc username, String JavaDoc password, ResultsFileWriteMonitor monitor) throws IOException JavaDoc, FailedLoginException JavaDoc {
974         return connect(url, username, password, monitor, null);
975     }
976
977     /**
978      * Does the meat of connecting to a URL. Can be used to just test the existance of
979      * something at the specified URL by passing the method 'HEAD'.
980      */

981     private static InputStream JavaDoc connect(URL JavaDoc url, String JavaDoc username, String JavaDoc password, ResultsFileWriteMonitor monitor, String JavaDoc method) throws IOException JavaDoc, FailedLoginException JavaDoc {
982         URLConnection JavaDoc con = url.openConnection();
983         if(con instanceof HttpURLConnection JavaDoc) {
984             HttpURLConnection JavaDoc http = (HttpURLConnection JavaDoc) url.openConnection();
985             if(method != null) {
986                 http.setRequestMethod(method);
987             }
988             http.connect();
989             if(http.getResponseCode() == 401) { // need to authenticate
990
if(username == null || username.equals("")) {
991                     throw new FailedLoginException JavaDoc("Server returned 401 "+http.getResponseMessage());
992                 }
993                 http = (HttpURLConnection JavaDoc) url.openConnection();
994                 http.setRequestProperty("Authorization", "Basic " + new String JavaDoc(Base64.encode((username + ":" + password).getBytes())));
995                 if(method != null) {
996                     http.setRequestMethod(method);
997                 }
998                 http.connect();
999                 if(http.getResponseCode() == 401) {
1000                    throw new FailedLoginException JavaDoc("Server returned 401 "+http.getResponseMessage());
1001                } else if(http.getResponseCode() == 404) {
1002                    return null; // Not found at this repository
1003
}
1004                if(monitor != null && http.getContentLength() > 0) {
1005                    monitor.setTotalBytes(http.getContentLength());
1006                }
1007                return http.getInputStream();
1008            } else if(http.getResponseCode() == 404) {
1009                return null; // Not found at this repository
1010
} else {
1011                if(monitor != null && http.getContentLength() > 0) {
1012                    monitor.setTotalBytes(http.getContentLength());
1013                }
1014                return http.getInputStream();
1015            }
1016        } else {
1017            if(username != null && !username.equals("")) {
1018                con.setRequestProperty("Authorization", "Basic " + new String JavaDoc(Base64.encode((username + ":" + password).getBytes())));
1019                try {
1020                    con.connect();
1021                    if(monitor != null && con.getContentLength() > 0) {
1022                        monitor.setTotalBytes(con.getContentLength());
1023                    }
1024                    return con.getInputStream();
1025                } catch (FileNotFoundException JavaDoc e) {
1026                    return null;
1027                }
1028            } else {
1029                try {
1030                    con.connect();
1031                    if(monitor != null && con.getContentLength() > 0) {
1032                        monitor.setTotalBytes(con.getContentLength());
1033                    }
1034                    return con.getInputStream();
1035                } catch (FileNotFoundException JavaDoc e) {
1036                    return null;
1037                }
1038            }
1039        }
1040    }
1041
1042    /**
1043     * Searches for an artifact in the listed repositories, where the artifact
1044     * may have wildcards in the ID.
1045     */

1046    private static Artifact findArtifact(Artifact query, URL JavaDoc[] repos, String JavaDoc username, String JavaDoc password, ResultsFileWriteMonitor monitor) throws MissingDependencyException {
1047        if(query.getGroupId() == null || query.getArtifactId() == null || query.getType() == null) {
1048            throw new MissingDependencyException("No support yet for dependencies missing more than a version: "+query);
1049        }
1050        List JavaDoc list = new ArrayList JavaDoc();
1051        for (int i = 0; i < repos.length; i++) {
1052            list.add(repos[i]);
1053        }
1054        Artifact result = null;
1055        for (int i = 0; i < list.size(); i++) {
1056            URL JavaDoc url = (URL JavaDoc) list.get(i);
1057            try {
1058                result = findArtifact(query, url, username, password, monitor);
1059            } catch (Exception JavaDoc e) {
1060                log.warn("Unable to read from "+url, e);
1061            }
1062            if(result != null) {
1063                return result;
1064            }
1065        }
1066        throw new MissingDependencyException("No repository has a valid artifact for "+query);
1067    }
1068
1069    /**
1070     * Checks for an artifact in a specific repository, where the artifact may
1071     * have wildcards in the ID.
1072     */

1073    private static Artifact findArtifact(Artifact query, URL JavaDoc url, String JavaDoc username, String JavaDoc password, ResultsFileWriteMonitor monitor) throws IOException JavaDoc, FailedLoginException JavaDoc, ParserConfigurationException JavaDoc, SAXException JavaDoc {
1074        monitor.getResults().setCurrentMessage("Searching for "+query+" at "+url);
1075        String JavaDoc base = query.getGroupId().replace('.', '/') + "/" + query.getArtifactId();
1076        String JavaDoc path = base +"/maven-metadata.xml";
1077        URL JavaDoc metaURL = new URL JavaDoc(url.toString().endsWith("/") ? url : new URL JavaDoc(url.toString()+"/"), path);
1078        InputStream JavaDoc in = connect(metaURL, username, password, monitor);
1079        if(in == null) {
1080            return null;
1081        }
1082        // Don't use the validating parser that we normally do
1083
DocumentBuilder JavaDoc builder = XmlUtil.newDocumentBuilderFactory().newDocumentBuilder();
1084        Document JavaDoc doc = builder.parse(in);
1085        Element JavaDoc root = doc.getDocumentElement();
1086        NodeList JavaDoc list = root.getElementsByTagName("versions");
1087        if(list.getLength() == 0) {
1088            return null;
1089        }
1090        list = ((Element JavaDoc)list.item(0)).getElementsByTagName("version");
1091        Version[] available = new Version[list.getLength()];
1092        for (int i = 0; i < available.length; i++) {
1093            available[i] = new Version(getText(list.item(i)));
1094        }
1095        Arrays.sort(available);
1096        for(int i=available.length-1; i>=0; i--) {
1097            Version version = available[i];
1098            URL JavaDoc metadataURL = new URL JavaDoc(url.toString()+base+"/"+version+"/maven-metadata.xml");
1099            InputStream JavaDoc metadataStream = connect(metadataURL, username, password, monitor);
1100            
1101            // check for a snapshot qualifier
1102
if (metadataStream != null) {
1103                DocumentBuilder JavaDoc metadatabuilder = XmlUtil.newDocumentBuilderFactory().newDocumentBuilder();
1104                Document JavaDoc metadatadoc = metadatabuilder.parse(metadataStream);
1105                NodeList JavaDoc snapshots = metadatadoc.getDocumentElement().getElementsByTagName("snapshot");
1106                if (snapshots.getLength() >= 1) {
1107                    Element JavaDoc snapshot = (Element JavaDoc)snapshots.item(0);
1108                    String JavaDoc[] timestamp = getChildrenText(snapshot, "timestamp");
1109                    String JavaDoc[] buildNumber = getChildrenText(snapshot, "buildNumber");
1110                    if (timestamp.length>=1 && buildNumber.length>=1) {
1111                        try {
1112                            SnapshotVersion snapshotVersion = new SnapshotVersion(version);
1113                            snapshotVersion.setBuildNumber(Integer.parseInt(buildNumber[0]));
1114                            snapshotVersion.setTimestamp(timestamp[0]);
1115                            version = snapshotVersion;
1116                        } catch (NumberFormatException JavaDoc nfe) {
1117                            log.warn("Could not create snapshot version for " + query);
1118                        }
1119                    }
1120                }
1121                metadataStream.close();
1122            }
1123            
1124            // look for the artifact in the maven repo
1125
Artifact verifiedArtifact = new Artifact(query.getGroupId(), query.getArtifactId(), version, query.getType());
1126            URL JavaDoc test = getURL(verifiedArtifact, url);
1127            InputStream JavaDoc testStream = connect(test, username, password, monitor, "HEAD");
1128            if(testStream == null) {
1129                log.warn("Maven repository "+url+" listed artifact "+query+" version "+version+" but I couldn't find it at "+test);
1130                continue;
1131            }
1132            testStream.close();
1133            return verifiedArtifact;
1134        }
1135        return null;
1136    }
1137
1138    /**
1139     * Puts the name and ID of a plugin into the argument map of plugins,
1140     * by reading the values out of the provided plugin descriptor file.
1141     *
1142     * @param xml The geronimo-plugin.xml for this plugin
1143     * @param plugins The result map to populate
1144     */

1145    private void readNameAndID(File JavaDoc xml, Map JavaDoc plugins) {
1146        try {
1147            SAXParserFactory JavaDoc factory = XmlUtil.newSAXParserFactory();
1148            SAXParser JavaDoc parser = factory.newSAXParser();
1149            PluginNameIDHandler handler = new PluginNameIDHandler();
1150            parser.parse(xml, handler);
1151            if(handler.isComplete()) {
1152                plugins.put(handler.getName(), Artifact.create(handler.getID()));
1153            }
1154        } catch (Exception JavaDoc e) {
1155            log.warn("Invalid XML at "+xml.getAbsolutePath(), e);
1156        }
1157    }
1158
1159    /**
1160     * Puts the name and ID of a plugin into the argument map of plugins,
1161     * by reading the values out of the provided plugin descriptor stream.
1162     *
1163     * @param xml The geronimo-plugin.xml for this plugin
1164     * @param plugins The result map to populate
1165     */

1166    private void readNameAndID(InputStream JavaDoc xml, Map JavaDoc plugins) {
1167        try {
1168            SAXParserFactory JavaDoc factory = XmlUtil.newSAXParserFactory();
1169            SAXParser JavaDoc parser = factory.newSAXParser();
1170            PluginNameIDHandler handler = new PluginNameIDHandler();
1171            parser.parse(xml, handler);
1172            if(handler.isComplete()) {
1173                plugins.put(handler.getName(), Artifact.create(handler.getID()));
1174            }
1175        } catch (Exception JavaDoc e) {
1176            log.warn("Invalid XML", e);
1177        }
1178    }
1179
1180    /**
1181     * Replaces all the dependency elements in the argument configuration data
1182     * with the dependencies from the actual data for that module.
1183     */

1184    private void overrideDependencies(ConfigurationData data, PluginMetadata metadata) {
1185        //todo: this ends up doing a little more work than necessary
1186
PluginMetadata temp = createDefaultMetadata(data);
1187        metadata.setDependencies(temp.getDependencies());
1188    }
1189
1190    /**
1191     * Generates a default plugin metadata based on the data for this module
1192     * in the server.
1193     */

1194    private PluginMetadata createDefaultMetadata(ConfigurationData data) {
1195        PluginMetadata meta = new PluginMetadata(data.getId().toString(), // name
1196
data.getId(), // module ID
1197
"Unknown", // category
1198
"Please provide a description",
1199                null, // URL
1200
null, // author
1201
null, // hash
1202
true, // installed
1203
false);
1204        meta.setGeronimoVersions(new String JavaDoc[]{serverInfo.getVersion()});
1205        meta.setJvmVersions(new String JavaDoc[0]);
1206        meta.setLicenses(new PluginMetadata.License[0]);
1207        meta.setObsoletes(new String JavaDoc[]{new Artifact(data.getId().getGroupId(), data.getId().getArtifactId(), (Version)null, data.getId().getType()).toString()});
1208        meta.setFilesToCopy(new PluginMetadata.CopyFile[0]);
1209        List JavaDoc deps = new ArrayList JavaDoc();
1210        PluginMetadata.Prerequisite prereq = null;
1211        prereq = processDependencyList(data.getEnvironment().getDependencies(), prereq, deps);
1212        Map JavaDoc children = data.getChildConfigurations();
1213        for (Iterator JavaDoc it = children.values().iterator(); it.hasNext();) {
1214            ConfigurationData child = (ConfigurationData) it.next();
1215            prereq = processDependencyList(child.getEnvironment().getDependencies(), prereq, deps);
1216        }
1217        meta.setDependencies((String JavaDoc[]) deps.toArray(new String JavaDoc[deps.size()]));
1218        meta.setPrerequisites(prereq == null ? new PluginMetadata.Prerequisite[0] : new PluginMetadata.Prerequisite[]{prereq});
1219        return meta;
1220    }
1221
1222    /**
1223     * Read the plugin metadata out of a plugin CAR file on disk.
1224     */

1225    private PluginMetadata loadCARFile(File JavaDoc file, boolean definitelyCAR) throws IOException JavaDoc, ParserConfigurationException JavaDoc, SAXException JavaDoc {
1226        if(!file.canRead()) {
1227            log.error("Cannot read from downloaded CAR file "+file.getAbsolutePath());
1228            return null;
1229        }
1230        JarFile JavaDoc jar = new JarFile JavaDoc(file);
1231        Document JavaDoc doc;
1232        try {
1233            JarEntry JavaDoc entry = jar.getJarEntry("META-INF/geronimo-plugin.xml");
1234            if(entry == null) {
1235                if(definitelyCAR) {
1236                    log.error("Downloaded CAR file does not contain META-INF/geronimo-plugin.xml file");
1237                }
1238                jar.close();
1239                return null;
1240            }
1241            InputStream JavaDoc in = jar.getInputStream(entry);
1242            DocumentBuilder JavaDoc builder = createDocumentBuilder();
1243            doc = builder.parse(in);
1244            in.close();
1245        } finally {
1246            jar.close();
1247        }
1248        return loadPluginMetadata(doc, file.getAbsolutePath());
1249    }
1250
1251    /**
1252     * Read a set of plugin metadata from a DOM document.
1253     */

1254    private PluginMetadata loadPluginMetadata(Document JavaDoc doc, String JavaDoc file) throws SAXException JavaDoc, MalformedURLException JavaDoc {
1255        Element JavaDoc root = doc.getDocumentElement();
1256        if(!root.getNodeName().equals("geronimo-plugin")) {
1257            log.error("Configuration archive "+file+" does not have a geronimo-plugin in META-INF/geronimo-plugin.xml");
1258            return null;
1259        }
1260        return processPlugin(root);
1261    }
1262
1263    /**
1264     * Loads the list of all available plugins from the specified stream
1265     * (representing geronimo-plugins.xml at the specified repository).
1266     */

1267    private PluginList loadPluginList(URL JavaDoc repo, InputStream JavaDoc in) throws ParserConfigurationException JavaDoc, IOException JavaDoc, SAXException JavaDoc {
1268        DocumentBuilder JavaDoc builder = createDocumentBuilder();
1269        Document JavaDoc doc = builder.parse(in);
1270        in.close();
1271        Element JavaDoc root = doc.getDocumentElement(); // geronimo-plugin-list
1272
NodeList JavaDoc configs = root.getElementsByTagName("plugin");
1273        List JavaDoc results = new ArrayList JavaDoc();
1274        for (int i = 0; i < configs.getLength(); i++) {
1275            Element JavaDoc config = (Element JavaDoc) configs.item(i);
1276            PluginMetadata data = processPlugin(config);
1277            results.add(data);
1278        }
1279        String JavaDoc[] repos = getChildrenText(root, "default-repository");
1280        URL JavaDoc[] repoURLs = new URL JavaDoc[repos.length];
1281        for(int i = 0; i < repos.length; i++) {
1282            if(repos[i].endsWith("/")) {
1283                repoURLs[i] = new URL JavaDoc(repos[i]);
1284            } else {
1285                repoURLs[i] = new URL JavaDoc(repos[i]+"/");
1286            }
1287        }
1288
1289        PluginMetadata[] data = (PluginMetadata[]) results.toArray(new PluginMetadata[results.size()]);
1290        return new PluginList(repoURLs, data);
1291    }
1292
1293    /**
1294     * Common logic for setting up a document builder to deal with plugin files.
1295     * @return
1296     * @throws ParserConfigurationException
1297     */

1298    private static DocumentBuilder JavaDoc createDocumentBuilder() throws ParserConfigurationException JavaDoc {
1299        DocumentBuilderFactory JavaDoc factory = XmlUtil.newDocumentBuilderFactory();
1300        factory.setValidating(true);
1301        factory.setNamespaceAware(true);
1302        factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
1303                             "http://www.w3.org/2001/XMLSchema");
1304        factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource",
1305                             new InputStream JavaDoc[]{
1306                                     PluginInstallerGBean.class.getResourceAsStream("/META-INF/schema/attributes-1.1.xsd"),
1307                                     PluginInstallerGBean.class.getResourceAsStream("/META-INF/schema/plugins-1.1.xsd"),
1308                             }
1309        );
1310        DocumentBuilder JavaDoc builder = factory.newDocumentBuilder();
1311        builder.setErrorHandler(new ErrorHandler JavaDoc() {
1312            public void error(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1313                throw new SAXException JavaDoc("Unable to read plugin file", exception);
1314            }
1315
1316            public void fatalError(SAXParseException JavaDoc exception) throws SAXException JavaDoc {
1317                throw new SAXException JavaDoc("Unable to read plugin file", exception);
1318            }
1319
1320            public void warning(SAXParseException JavaDoc exception) {
1321                log.warn("Warning reading XML document", exception);
1322            }
1323        });
1324        return builder;
1325    }
1326
1327    /**
1328     * Given a DOM element representing a plugin, load it into a PluginMetadata
1329     * object.
1330     */

1331    private PluginMetadata processPlugin(Element JavaDoc plugin) throws SAXException JavaDoc, MalformedURLException JavaDoc {
1332        String JavaDoc moduleId = getChildText(plugin, "module-id");
1333        NodeList JavaDoc licenseNodes = plugin.getElementsByTagName("license");
1334        PluginMetadata.License[] licenses = new PluginMetadata.License[licenseNodes.getLength()];
1335        for(int j=0; j<licenseNodes.getLength(); j++) {
1336            Element JavaDoc node = (Element JavaDoc) licenseNodes.item(j);
1337            String JavaDoc licenseName = getText(node);
1338            String JavaDoc openSource = node.getAttribute("osi-approved");
1339            if(licenseName == null || licenseName.equals("") || openSource == null || openSource.equals("")) {
1340                throw new SAXException JavaDoc("Invalid config file: license name and osi-approved flag required");
1341            }
1342            licenses[j] = new PluginMetadata.License(licenseName, Boolean.valueOf(openSource).booleanValue());
1343        }
1344        PluginMetadata.Hash hash = null;
1345        NodeList JavaDoc hashList = plugin.getElementsByTagName("hash");
1346        if(hashList.getLength() > 0) {
1347            Element JavaDoc elem = (Element JavaDoc) hashList.item(0);
1348            hash = new PluginMetadata.Hash(elem.getAttribute("type"), getText(elem));
1349        }
1350        NodeList JavaDoc fileList = plugin.getElementsByTagName("copy-file");
1351        PluginMetadata.CopyFile[] files = new PluginMetadata.CopyFile[fileList.getLength()];
1352        for (int i = 0; i < files.length; i++) {
1353            Element JavaDoc node = (Element JavaDoc) fileList.item(i);
1354            String JavaDoc relative = node.getAttribute("relative-to");
1355            String JavaDoc destDir = node.getAttribute("dest-dir");
1356            String JavaDoc fileName = getText(node);
1357            files[i] = new PluginMetadata.CopyFile(relative.equals("server"), fileName, destDir);
1358        }
1359        NodeList JavaDoc gbeans = plugin.getElementsByTagName("gbean");
1360        GBeanOverride[] overrides = new GBeanOverride[gbeans.getLength()];
1361        for (int i = 0; i < overrides.length; i++) {
1362            Element JavaDoc node = (Element JavaDoc) gbeans.item(i);
1363            try {
1364                overrides[i] = new GBeanOverride(node);
1365            } catch (InvalidGBeanException e) {
1366                log.error("Unable to process config.xml entry "+node.getAttribute("name")+" ("+node+")", e);
1367            }
1368        }
1369        boolean eligible = true;
1370        NodeList JavaDoc preNodes = plugin.getElementsByTagName("prerequisite");
1371        PluginMetadata.Prerequisite[] prereqs = new PluginMetadata.Prerequisite[preNodes.getLength()];
1372        for(int j=0; j<preNodes.getLength(); j++) {
1373            Element JavaDoc node = (Element JavaDoc) preNodes.item(j);
1374            String JavaDoc originalConfigId = getChildText(node, "id");
1375            if(originalConfigId == null) {
1376                throw new SAXException JavaDoc("Prerequisite requires <id>");
1377            }
1378            Artifact artifact = Artifact.create(originalConfigId.replaceAll("\\*", ""));
1379            boolean present = resolver.queryArtifacts(artifact).length > 0;
1380            prereqs[j] = new PluginMetadata.Prerequisite(artifact, present,
1381                    getChildText(node, "resource-type"), getChildText(node, "description"));
1382            if(!present) {
1383                log.debug(moduleId+" is not eligible due to missing "+prereqs[j].getModuleId());
1384                eligible = false;
1385            }
1386        }
1387        String JavaDoc[] gerVersions = getChildrenText(plugin, "geronimo-version");
1388        if(gerVersions.length > 0) {
1389            boolean match = checkGeronimoVersions(gerVersions);
1390            if(!match) eligible = false;
1391        }
1392        String JavaDoc[] jvmVersions = getChildrenText(plugin, "jvm-version");
1393        if(jvmVersions.length > 0) {
1394            boolean match = checkJVMVersions(jvmVersions);
1395            if(!match) eligible = false;
1396        }
1397        String JavaDoc[] repoNames = getChildrenText(plugin, "source-repository");
1398        URL JavaDoc[] repos = new URL JavaDoc[repoNames.length];
1399        for (int i = 0; i < repos.length; i++) {
1400            repos[i] = new URL JavaDoc(repoNames[i]);
1401        }
1402        Artifact artifact = null;
1403        boolean installed = false;
1404        if (moduleId != null) {
1405            artifact = Artifact.create(moduleId);
1406            // Tests, etc. don't need to have a ConfigurationManager
1407
installed = configManager != null && configManager.isInstalled(artifact);
1408        }
1409        log.trace("Checking "+moduleId+": installed="+installed+", eligible="+eligible);
1410        PluginMetadata data = new PluginMetadata(getChildText(plugin, "name"),
1411                artifact,
1412                getChildText(plugin, "category"),
1413                getChildText(plugin, "description"),
1414                getChildText(plugin, "url"),
1415                getChildText(plugin, "author"),
1416                hash,
1417                installed, eligible);
1418        data.setGeronimoVersions(gerVersions);
1419        data.setJvmVersions(jvmVersions);
1420        data.setLicenses(licenses);
1421        data.setPrerequisites(prereqs);
1422        data.setRepositories(repos);
1423        data.setFilesToCopy(files);
1424        data.setConfigXmls(overrides);
1425        NodeList JavaDoc list = plugin.getElementsByTagName("dependency");
1426        List JavaDoc start = new ArrayList JavaDoc();
1427        String JavaDoc deps[] = new String JavaDoc[list.getLength()];
1428        for(int i=0; i<list.getLength(); i++) {
1429            Element JavaDoc node = (Element JavaDoc) list.item(i);
1430            deps[i] = getText(node);
1431            if(node.hasAttribute("start") && node.getAttribute("start").equalsIgnoreCase("true")) {
1432                start.add(deps[i]);
1433            }
1434        }
1435        data.setDependencies(deps);
1436        data.setForceStart((String JavaDoc[]) start.toArray(new String JavaDoc[start.size()]));
1437        data.setObsoletes(getChildrenText(plugin, "obsoletes"));
1438        return data;
1439    }
1440
1441    /**
1442     * Check whether the specified JVM versions match the current runtime
1443     * environment.
1444     *
1445     * @return true if the specified versions match the current
1446     * execution environment as defined by plugins-1.1.xsd
1447     */

1448    private boolean checkJVMVersions(String JavaDoc[] jvmVersions) {
1449        if(jvmVersions.length == 0) return true;
1450        String JavaDoc version = System.getProperty("java.version");
1451        boolean match = false;
1452        for (int j = 0; j < jvmVersions.length; j++) {
1453            String JavaDoc jvmVersion = jvmVersions[j];
1454            if(jvmVersion == null || jvmVersion.equals("")) {
1455                throw new IllegalStateException JavaDoc("jvm-version should not be empty!");
1456            }
1457            if(version.startsWith(jvmVersion)) {
1458                match = true;
1459                break;
1460            }
1461        }
1462        return match;
1463    }
1464
1465    /**
1466     * Check whether the specified Geronimo versions match the current runtime
1467     * environment.
1468     *
1469     * @return true if the specified versions match the current
1470     * execution environment as defined by plugins-1.1.xsd
1471     */

1472    private boolean checkGeronimoVersions(String JavaDoc[] gerVersions) {
1473        if(gerVersions.length == 0) return true;
1474        String JavaDoc version = serverInfo.getVersion();
1475        boolean match = false;
1476        for (int j = 0; j < gerVersions.length; j++) {
1477            String JavaDoc gerVersion = gerVersions[j];
1478            if(gerVersion == null || gerVersion.equals("")) {
1479                throw new IllegalStateException JavaDoc("geronimo-version should not be empty!");
1480            }
1481            if(gerVersion.equals(version)) {
1482                match = true;
1483                break;
1484            }
1485        }
1486        return match;
1487    }
1488
1489    /**
1490     * Gets the text out of a child of the specified DOM element.
1491     *
1492     * @param root The parent DOM element
1493     * @param property The name of the child element that holds the text
1494     */

1495    private static String JavaDoc getChildText(Element JavaDoc root, String JavaDoc property) {
1496        NodeList JavaDoc children = root.getChildNodes();
1497        for(int i=0; i<children.getLength(); i++) {
1498            Node JavaDoc check = children.item(i);
1499            if(check.getNodeType() == Node.ELEMENT_NODE && check.getNodeName().equals(property)) {
1500                return getText(check);
1501            }
1502        }
1503        return null;
1504    }
1505
1506    /**
1507     * Gets all the text contents of the specified DOM node.
1508     */

1509    private static String JavaDoc getText(Node JavaDoc target) {
1510        NodeList JavaDoc nodes = target.getChildNodes();
1511        StringBuffer JavaDoc buf = null;
1512        for(int j=0; j<nodes.getLength(); j++) {
1513            Node JavaDoc node = nodes.item(j);
1514            if(node.getNodeType() == Node.TEXT_NODE) {
1515                if(buf == null) {
1516                    buf = new StringBuffer JavaDoc();
1517                }
1518                buf.append(node.getNodeValue());
1519            }
1520        }
1521        return buf == null ? null : buf.toString();
1522    }
1523
1524    /**
1525     * Gets the text out of all the child nodes of a certain type. The result
1526     * array has one element for each child of the specified DOM element that
1527     * has the specified name.
1528     *
1529     * @param root The parent DOM element
1530     * @param property The name of the child elements that hold the text
1531     */

1532    private static String JavaDoc[] getChildrenText(Element JavaDoc root, String JavaDoc property) {
1533        NodeList JavaDoc children = root.getChildNodes();
1534        List JavaDoc results = new ArrayList JavaDoc();
1535        for(int i=0; i<children.getLength(); i++) {
1536            Node JavaDoc check = children.item(i);
1537            if(check.getNodeType() == Node.ELEMENT_NODE && check.getNodeName().equals(property)) {
1538                NodeList JavaDoc nodes = check.getChildNodes();
1539                StringBuffer JavaDoc buf = null;
1540                for(int j=0; j<nodes.getLength(); j++) {
1541                    Node JavaDoc node = nodes.item(j);
1542                    if(node.getNodeType() == Node.TEXT_NODE) {
1543                        if(buf == null) {
1544                            buf = new StringBuffer JavaDoc();
1545                        }
1546                        buf.append(node.getNodeValue());
1547                    }
1548                }
1549                results.add(buf == null ? null : buf.toString());
1550            }
1551        }
1552        return (String JavaDoc[]) results.toArray(new String JavaDoc[results.size()]);
1553    }
1554
1555    /**
1556     * Generates dependencies and an optional prerequisite based on a list of
1557     * dependencies for a Gernonimo module.
1558     *
1559     * @param real A list with elements of type Dependency
1560     * @param prereq The incoming prerequisite (if any), which may be replaced
1561     * @param deps A list with elements of type String (holding a module ID / Artifact name)
1562     *
1563     * @return The resulting prerequisite, if any.
1564     */

1565    private PluginMetadata.Prerequisite processDependencyList(List JavaDoc real, PluginMetadata.Prerequisite prereq, List JavaDoc deps) {
1566        for (int i = 0; i < real.size(); i++) {
1567            Dependency dep = (Dependency) real.get(i);
1568            if(dep.getArtifact().getGroupId().equals("geronimo")) {
1569                if(dep.getArtifact().getArtifactId().indexOf("jetty") > -1) {
1570                    if(prereq == null) {
1571                        prereq = new PluginMetadata.Prerequisite(dep.getArtifact(), true, "Web Container", "This plugin works with the Geronimo/Jetty distribution. It is not intended to run in the Geronimo/Tomcat distribution. There is a separate version of this plugin that works with Tomcat.");
1572                    }
1573                    continue;
1574                } else if(dep.getArtifact().getArtifactId().indexOf("tomcat") > -1) {
1575                    if(prereq == null) {
1576                        prereq = new PluginMetadata.Prerequisite(dep.getArtifact(), true, "Web Container", "This plugin works with the Geronimo/Tomcat distribution. It is not intended to run in the Geronimo/Jetty distribution. There is a separate version of this plugin that works with Jetty.");
1577                    }
1578                    continue;
1579                }
1580            }
1581            if(!deps.contains(dep.getArtifact().toString())) {
1582                deps.add(dep.getArtifact().toString());
1583            }
1584        }
1585        return prereq;
1586    }
1587
1588    /**
1589     * Writes plugin metadata to a DOM tree.
1590     */

1591    private static Document JavaDoc writePluginMetadata(PluginMetadata data) throws ParserConfigurationException JavaDoc {
1592        DocumentBuilder JavaDoc builder = createDocumentBuilder();
1593        Document JavaDoc doc = builder.newDocument();
1594        Element JavaDoc config = doc.createElementNS("http://geronimo.apache.org/xml/ns/plugins-1.1", "geronimo-plugin");
1595        config.setAttribute("xmlns", "http://geronimo.apache.org/xml/ns/plugins-1.1");
1596        doc.appendChild(config);
1597
1598        addTextChild(doc, config, "name", data.getName());
1599        addTextChild(doc, config, "module-id", data.getModuleId().toString());
1600        addTextChild(doc, config, "category", data.getCategory());
1601        addTextChild(doc, config, "description", data.getDescription());
1602        if(data.getPluginURL() != null) {
1603            addTextChild(doc, config, "url", data.getPluginURL());
1604        }
1605        if(data.getAuthor() != null) {
1606            addTextChild(doc, config, "author", data.getAuthor());
1607        }
1608        for (int i = 0; i < data.getLicenses().length; i++) {
1609            PluginMetadata.License license = data.getLicenses()[i];
1610            Element JavaDoc lic = doc.createElement("license");
1611            lic.appendChild(doc.createTextNode(license.getName()));
1612            lic.setAttribute("osi-approved", Boolean.toString(license.isOsiApproved()));
1613            config.appendChild(lic);
1614        }
1615        if(data.getHash() != null) {
1616            Element JavaDoc hash = doc.createElement("hash");
1617            hash.setAttribute("type", data.getHash().getType());
1618            hash.appendChild(doc.createTextNode(data.getHash().getValue()));
1619            config.appendChild(hash);
1620        }
1621        for (int i = 0; i < data.getGeronimoVersions().length; i++) {
1622            addTextChild(doc, config, "geronimo-version", data.getGeronimoVersions()[i]);
1623        }
1624        for (int i = 0; i < data.getJvmVersions().length; i++) {
1625            addTextChild(doc, config, "jvm-version", data.getJvmVersions()[i]);
1626        }
1627        for (int i = 0; i < data.getPrerequisites().length; i++) {
1628            PluginMetadata.Prerequisite prereq = data.getPrerequisites()[i];
1629            Element JavaDoc pre = doc.createElement("prerequisite");
1630            addTextChild(doc, pre, "id", prereq.getModuleId().toString());
1631            if(prereq.getResourceType() != null) {
1632                addTextChild(doc, pre, "resource-type", prereq.getResourceType());
1633            }
1634            if(prereq.getDescription() != null) {
1635                addTextChild(doc, pre, "description", prereq.getDescription());
1636            }
1637            config.appendChild(pre);
1638        }
1639        for (int i = 0; i < data.getDependencies().length; i++) {
1640            addTextChild(doc, config, "dependency", data.getDependencies()[i]);
1641        }
1642        for (int i = 0; i < data.getObsoletes().length; i++) {
1643            addTextChild(doc, config, "obsoletes", data.getObsoletes()[i]);
1644        }
1645        for (int i = 0; i < data.getRepositories().length; i++) {
1646            URL JavaDoc url = data.getRepositories()[i];
1647            addTextChild(doc, config, "source-repository", url.toString());
1648        }
1649        for (int i = 0; i < data.getFilesToCopy().length; i++) {
1650            PluginMetadata.CopyFile file = data.getFilesToCopy()[i];
1651            Element JavaDoc copy = doc.createElement("copy-file");
1652            copy.setAttribute("relative-to", file.isRelativeToVar() ? "server" : "geronimo");
1653            copy.setAttribute("dest-dir", file.getDestDir());
1654            copy.appendChild(doc.createTextNode(file.getSourceFile()));
1655            config.appendChild(copy);
1656        }
1657        if(data.getConfigXmls().length > 0) {
1658            Element JavaDoc content = doc.createElement("config-xml-content");
1659            for (int i = 0; i < data.getConfigXmls().length; i++) {
1660                GBeanOverride override = data.getConfigXmls()[i];
1661                Element JavaDoc gbean = override.writeXml(doc, content);
1662                gbean.setAttribute("xmlns", "http://geronimo.apache.org/xml/ns/attributes-1.1");
1663            }
1664            config.appendChild(content);
1665        }
1666        return doc;
1667    }
1668
1669    /**
1670     * Adds a child of the specified Element that just has the specified text content
1671     * @param doc The document
1672     * @param parent The parent element
1673     * @param name The name of the child element to add
1674     * @param text The contents of the child element to add
1675     */

1676    private static void addTextChild(Document JavaDoc doc, Element JavaDoc parent, String JavaDoc name, String JavaDoc text) {
1677        Element JavaDoc child = doc.createElement(name);
1678        child.appendChild(doc.createTextNode(text));
1679        parent.appendChild(child);
1680    }
1681
1682    /**
1683     * If a plugin includes config.xml content, copy it into the attribute
1684     * store.
1685     */

1686    private void installConfigXMLData(Artifact configID, PluginMetadata pluginData) {
1687        if(configManager.isConfiguration(configID) && attributeStore != null
1688                && pluginData != null && pluginData.getConfigXmls().length > 0) {
1689            attributeStore.setModuleGBeans(configID, pluginData.getConfigXmls());
1690        }
1691    }
1692
1693    /**
1694     * Gets a token unique to this run of the server, used to track asynchronous
1695     * downloads.
1696     */

1697    private static Object JavaDoc getNextKey() {
1698        int value;
1699        synchronized(PluginInstallerGBean.class) {
1700            value = ++counter;
1701        }
1702        return new Integer JavaDoc(value);
1703    }
1704
1705    /**
1706     * Helper clas to extract a name and module ID from a plugin metadata file.
1707     */

1708    private static class PluginNameIDHandler extends DefaultHandler JavaDoc {
1709        private String JavaDoc id = "";
1710        private String JavaDoc name = "";
1711        private String JavaDoc element = null;
1712
1713        public void characters(char ch[], int start, int length) throws SAXException JavaDoc {
1714            if(element != null) {
1715                if(element.equals("module-id")) {
1716                    id += new String JavaDoc(ch, start, length);
1717                } else if(element.equals("name")) {
1718                    name += new String JavaDoc(ch, start, length);
1719                }
1720            }
1721        }
1722
1723        public void endElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName) throws SAXException JavaDoc {
1724            element = null;
1725        }
1726
1727        public void startElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc attributes) throws SAXException JavaDoc {
1728            if(qName.equals("module-id") || qName.equals("name")) {
1729                element = qName;
1730            }
1731        }
1732
1733        public void endDocument() throws SAXException JavaDoc {
1734            id = id.trim();
1735            name = name.trim();
1736        }
1737
1738        public String JavaDoc getID() {
1739            return id;
1740        }
1741
1742        public String JavaDoc getName() {
1743            return name;
1744        }
1745
1746        public boolean isComplete() {
1747            return !id.equals("") && !name.equals("");
1748        }
1749    }
1750
1751    /**
1752     * Helper class to bridge a FileWriteMonitor to a DownloadPoller.
1753     */

1754    private static class ResultsFileWriteMonitor implements FileWriteMonitor {
1755        private final DownloadPoller results;
1756        private int totalBytes;
1757        private String JavaDoc file;
1758
1759        public ResultsFileWriteMonitor(DownloadPoller results) {
1760            this.results = results;
1761        }
1762
1763        public void setTotalBytes(int totalBytes) {
1764            this.totalBytes = totalBytes;
1765        }
1766
1767        public int getTotalBytes() {
1768            return totalBytes;
1769        }
1770
1771        public void writeStarted(String JavaDoc fileDescription, int fileSize) {
1772            totalBytes = fileSize;
1773            file = fileDescription;
1774            results.setCurrentFile(fileDescription);
1775            results.setCurrentFilePercent(totalBytes > 0 ? 0 : -1);
1776        }
1777
1778        public void writeProgress(int bytes) {
1779            if(totalBytes > 0) {
1780                double percent = (double)bytes/(double)totalBytes;
1781                results.setCurrentFilePercent((int)(percent*100));
1782            } else {
1783                results.setCurrentMessage((bytes/1024)+" kB of "+file);
1784            }
1785        }
1786
1787        public void writeComplete(int bytes) {
1788            results.setCurrentFilePercent(100);
1789            results.setCurrentMessage("Finished installing "+file+" ("+(bytes/1024)+" kB)");
1790            results.addDownloadBytes(bytes);
1791        }
1792
1793        public DownloadPoller getResults() {
1794            return results;
1795        }
1796    }
1797
1798    /**
1799     * Interesting data resulting from opening a connection to a remote file.
1800     */

1801    private static class OpenResult {
1802        private final InputStream JavaDoc stream;
1803        private final Artifact configID;
1804        private final int fileSize;
1805
1806        public OpenResult(Artifact configID, InputStream JavaDoc stream, int fileSize) {
1807            this.configID = configID;
1808            this.stream = stream;
1809            this.fileSize = fileSize;
1810        }
1811
1812        public Artifact getConfigID() {
1813            return configID;
1814        }
1815
1816        public InputStream JavaDoc getStream() {
1817            return stream;
1818        }
1819
1820        public int getFileSize() {
1821            return fileSize;
1822        }
1823    }
1824
1825    public static final GBeanInfo GBEAN_INFO;
1826
1827    static {
1828        GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(PluginInstallerGBean.class);
1829        infoFactory.addReference("ConfigManager", ConfigurationManager.class, "ConfigurationManager");
1830        infoFactory.addReference("Repository", WritableListableRepository.class, "Repository");
1831        infoFactory.addReference("ConfigStore", ConfigurationStore.class, "ConfigurationStore");
1832        infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
1833        infoFactory.addReference("ThreadPool", ThreadPool.class, "GBean");
1834        infoFactory.addReference("PluginAttributeStore", PluginAttributeStore.class, "AttributeStore");
1835        infoFactory.addInterface(PluginInstaller.class);
1836
1837        infoFactory.setConstructor(new String JavaDoc[]{"ConfigManager", "Repository", "ConfigStore",
1838                                                "ServerInfo", "ThreadPool", "PluginAttributeStore"});
1839
1840        GBEAN_INFO = infoFactory.getBeanInfo();
1841    }
1842
1843    public static GBeanInfo getGBeanInfo() {
1844        return GBEAN_INFO;
1845    }
1846}
1847
Popular Tags