1 17 package org.apache.geronimo.system.plugin; 18 19 import java.io.File ; 20 import java.io.FileNotFoundException ; 21 import java.io.FileOutputStream ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.BufferedOutputStream ; 25 import java.net.HttpURLConnection ; 26 import java.net.MalformedURLException ; 27 import java.net.URL ; 28 import java.net.URLConnection ; 29 import java.util.ArrayList ; 30 import java.util.Arrays ; 31 import java.util.Collection ; 32 import java.util.Collections ; 33 import java.util.HashMap ; 34 import java.util.HashSet ; 35 import java.util.Iterator ; 36 import java.util.LinkedList ; 37 import java.util.List ; 38 import java.util.Map ; 39 import java.util.Set ; 40 import java.util.SortedSet ; 41 import java.util.Enumeration ; 42 import java.util.jar.JarEntry ; 43 import java.util.jar.JarFile ; 44 import java.util.jar.JarOutputStream ; 45 import java.util.jar.Manifest ; 46 import java.util.zip.ZipEntry ; 47 import javax.security.auth.login.FailedLoginException ; 48 import javax.xml.parsers.DocumentBuilder ; 49 import javax.xml.parsers.DocumentBuilderFactory ; 50 import javax.xml.parsers.ParserConfigurationException ; 51 import javax.xml.parsers.SAXParser ; 52 import javax.xml.parsers.SAXParserFactory ; 53 import javax.xml.transform.OutputKeys ; 54 import javax.xml.transform.Transformer ; 55 import javax.xml.transform.TransformerFactory ; 56 import javax.xml.transform.dom.DOMSource ; 57 import javax.xml.transform.stream.StreamResult ; 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 ; 86 import org.w3c.dom.Element ; 87 import org.w3c.dom.Node ; 88 import org.w3c.dom.NodeList ; 89 import org.xml.sax.Attributes ; 90 import org.xml.sax.ErrorHandler ; 91 import org.xml.sax.SAXException ; 92 import org.xml.sax.SAXParseException ; 93 import org.xml.sax.helpers.DefaultHandler ; 94 95 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 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 ()); 120 attributeStore = store; 121 } 122 123 130 public Map getInstalledPlugins() { 131 SortedSet artifacts = writeableRepo.list(); 132 133 Map plugins = new HashMap (); 134 for (Iterator i = artifacts.iterator(); i.hasNext();) { 135 Artifact configId = (Artifact) i.next(); 136 File dir = writeableRepo.getLocation(configId); 137 if(dir.isDirectory()) { 138 File meta = new File (dir, "META-INF"); 139 if(!meta.isDirectory() || !meta.canRead()) { 140 continue; 141 } 142 File xml = new File (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 ("Cannot read artifact dir "+dir.getAbsolutePath()); 150 } 151 try { 152 JarFile jar = new JarFile (dir); 153 try { 154 ZipEntry entry = jar.getEntry("META-INF/geronimo-plugin.xml"); 155 if(entry == null) { 156 continue; 157 } 158 InputStream in = jar.getInputStream(entry); 159 readNameAndID(in, plugins); 160 in.close(); 161 } finally { 162 jar.close(); 163 } 164 } catch (IOException e) { 165 log.error("Unable to read JAR file "+dir.getAbsolutePath(), e); 166 } 167 } 168 } 169 return plugins; 170 } 171 172 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 dir = writeableRepo.getLocation(moduleId); 192 Document doc; 193 ConfigurationData configData; 194 String source = dir.getAbsolutePath(); 195 try { 196 if(dir.isDirectory()) { 197 File meta = new File (dir, "META-INF"); 198 if(!meta.isDirectory() || !meta.canRead()) { 199 return null; 200 } 201 File xml = new File (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 builder = createDocumentBuilder(); 208 doc = builder.parse(xml); 209 } else { 210 if(!dir.isFile() || !dir.canRead()) { 211 throw new IllegalStateException ("Cannot read configuration "+dir.getAbsolutePath()); 212 } 213 configData = configStore.loadConfiguration(moduleId); 214 JarFile jar = new JarFile (dir); 215 try { 216 ZipEntry 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 in = jar.getInputStream(entry); 222 DocumentBuilder 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 e) { 236 e.printStackTrace(); 237 log.warn("Invalid XML at "+source, e); 238 } 239 return null; 240 } 241 242 252 public void updatePluginMetadata(PluginMetadata metadata) { 253 File dir = writeableRepo.getLocation(metadata.getModuleId()); 254 if(dir == null) { 255 throw new IllegalArgumentException (metadata.getModuleId()+" is not installed!"); 256 } 257 if(!dir.isDirectory()) { try { 259 File temp = new File (dir.getParentFile(), dir.getName()+".temp"); 260 JarFile input = new JarFile (dir); 261 Manifest manifest = input.getManifest(); 262 JarOutputStream out = manifest == null ? new JarOutputStream (new BufferedOutputStream (new FileOutputStream (temp))) 263 : new JarOutputStream (new BufferedOutputStream (new FileOutputStream (temp)), manifest); 264 Enumeration en = input.entries(); 265 byte[] buf = new byte[4096]; 266 int count; 267 while (en.hasMoreElements()) { 268 JarEntry entry = (JarEntry ) en.nextElement(); 269 if(entry.getName().equals("META-INF/geronimo-plugin.xml")) { 270 entry = new JarEntry (entry.getName()); 271 out.putNextEntry(entry); 272 Document doc = writePluginMetadata(metadata); 273 TransformerFactory xfactory = XmlUtil.newTransformerFactory(); 274 Transformer 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 (doc), new StreamResult (out)); 278 } else if(entry.getName().equals("META-INF/MANIFEST.MF")) { 279 } else { 281 out.putNextEntry(entry); 282 InputStream 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 ("Unable to delete old plugin at "+dir.getAbsolutePath()); 295 } 296 if(!temp.renameTo(dir)) { 297 throw new IOException ("Unable to move new plugin "+temp.getAbsolutePath()+" to "+dir.getAbsolutePath()); 298 } 299 } catch (Exception e) { 300 log.error("Unable to update plugin metadata", e); 301 throw new RuntimeException ("Unable to update plugin metadata", e); 302 } } else { 304 File meta = new File (dir, "META-INF"); 305 if(!meta.isDirectory() || !meta.canRead()) { 306 throw new IllegalArgumentException (metadata.getModuleId()+" is not a plugin!"); 307 } 308 File xml = new File (meta, "geronimo-plugin.xml"); 309 FileOutputStream fos = null; 310 try { 311 if(!xml.isFile()) { 312 if(!xml.createNewFile()) { 313 throw new RuntimeException ("Cannot create plugin metadata file for "+metadata.getModuleId()); 314 } 315 } 316 Document doc = writePluginMetadata(metadata); 317 TransformerFactory xfactory = XmlUtil.newTransformerFactory(); 318 Transformer xform = xfactory.newTransformer(); 319 xform.setOutputProperty(OutputKeys.INDENT, "yes"); 320 xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); 321 fos = new FileOutputStream (xml); 322 StreamResult sr = new StreamResult (fos); 325 xform.transform(new DOMSource (doc), sr); 326 } catch (Exception 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 ignored) { 333 } 335 } 336 } 337 } 338 } 339 340 350 public PluginList listPlugins(URL mavenRepository, String username, String password) throws IOException , FailedLoginException { 351 String repository = mavenRepository.toString(); 352 if(!repository.endsWith("/")) { 353 repository = repository+"/"; 354 } 355 URL url = new URL (repository+"geronimo-plugins.xml"); 357 try { 358 InputStream in = openStream(null, new URL []{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 e) { 365 log.error("Unable to load repository configuration data", e); 366 return null; 367 } 368 } 369 370 382 public DownloadResults install(PluginList pluginsToInstall, String username, String password) { 383 DownloadResults results = new DownloadResults(); 384 install(pluginsToInstall, username, password, results); 385 return results; 386 } 387 388 404 public void install(PluginList pluginsToInstall, String username, String password, DownloadPoller poller) { 405 try { 406 Map metaMap = new HashMap (); 407 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 for (int i = 0; i < pluginsToInstall.getPlugins().length; i++) { 418 PluginMetadata metadata = pluginsToInstall.getPlugins()[i]; 420 List obsoletes = new ArrayList (); 422 for (int j = 0; j < metadata.getObsoletes().length; j++) { 423 String 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 Set working = new HashSet (); 439 if(metadata.getModuleId() != null) { 440 URL [] 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 [] deps = metadata.getDependencies(); 448 for (int j = 0; j < deps.length; j++) { 449 String dep = deps[j]; 450 Artifact entry = Artifact.create(dep); 451 URL [] 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 for (int j = 0; j < obsoletes.size(); j++) { 461 Artifact artifact = (Artifact) obsoletes.get(j); 462 configManager.uninstallConfiguration(artifact); 463 } 464 } 466 467 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 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 e) { 482 poller.setFailure(e); 483 } finally { 484 poller.setFinished(); 485 } 486 } 487 488 503 public Object startInstall(final PluginList pluginsToInstall, final String username, final String password) { 504 Object key = getNextKey(); 505 final DownloadResults results = new DownloadResults(); 506 Runnable work = new Runnable () { 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 e) { 515 throw new RuntimeException ("Unable to start work", e); 516 } 517 return key; 518 } 519 520 540 public Object startInstall(final File carFile, final String username, final String password) { 541 Object key = getNextKey(); 542 final DownloadResults results = new DownloadResults(); 543 Runnable work = new Runnable () { 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 e) { 552 throw new RuntimeException ("Unable to start work", e); 553 } 554 return key; 555 } 556 557 565 public DownloadResults checkOnInstall(Object 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 577 public void install(File carFile, String username, String password, DownloadPoller poller) { 578 try { 579 PluginMetadata data = loadCARFile(carFile, true); 581 if(data == null) { 582 throw new IllegalArgumentException ("Invalid Configuration Archive "+carFile.getAbsolutePath()+" see server log for details"); 583 } 584 585 validatePlugin(data); 587 588 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 install(new PluginList(data.getRepositories(), new PluginMetadata[]{data}), 601 username, password, poller); 602 } catch (Exception e) { 603 poller.setFailure(e); 604 } finally { 605 poller.setFinished(); 606 } 607 } 608 609 612 private void validatePlugin(PluginMetadata metadata) throws MissingDependencyException { 613 if(metadata.getModuleId() != null) { if(configManager.isRunning(metadata.getModuleId())) { 616 boolean upgrade = false; 617 for (int i = 0; i < metadata.getObsoletes().length; i++) { 618 String 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 ("Configuration "+metadata.getModuleId()+" is already running!"); 627 } 628 } 629 } 630 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 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 664 private void downloadArtifact(Artifact configID, Map metadata, URL [] repos, String username, String password, ResultsFileWriteMonitor monitor, Set soFar, boolean dependency) throws IOException , FailedLoginException , MissingDependencyException { 665 if(soFar.contains(configID)) { 666 return; } else { 668 soFar.add(configID); 669 } 670 boolean pluginWasInstalled = false; 672 Artifact[] matches = configManager.getArtifactResolver().queryArtifacts(configID); 673 if(matches.length == 0) { monitor.getResults().setCurrentMessage("Downloading " + configID); 675 monitor.getResults().setCurrentFilePercent(-1); 676 OpenResult result = openStream(configID, repos, username, password, monitor); 677 try { 678 File tempFile = downloadFile(result, monitor); 679 if (tempFile == null) { 680 log.error("Null filehandle was returned for "+configID); 681 throw new IllegalArgumentException ("Null filehandle was returned for "+configID); 682 } 683 PluginMetadata pluginData = ((PluginMetadata) metadata.get(configID)); 684 PluginMetadata.Hash hash = pluginData == null ? null : pluginData.getHash(); 686 if(hash != null) { 687 String actual = ConfigurationStoreUtil.getActualChecksum(tempFile, hash.getType()); 688 if(!actual.equals(hash.getValue())) { 689 throw new IOException ("File download incorrect (expected "+hash.getType()+" hash "+hash.getValue()+" but got "+actual+")"); 690 } 691 } 692 if(pluginData == null) { 694 try { 695 pluginData = loadCARFile(tempFile, false); 696 } catch (Exception e) { 697 throw new IOException ("Unable to read plugin metadata: "+e.getMessage()); 698 } 699 } 700 if(pluginData != null) { validatePlugin(pluginData); 702 } 703 monitor.getResults().setCurrentMessage("Copying " + result.getConfigID() + " to the repository"); 704 writeableRepo.copyToRepository(tempFile, result.getConfigID(), monitor); 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 try { 733 ConfigurationData data = null; 734 if(!configID.isResolved()) { 735 for (int i = matches.length-1; i >= 0; i--) { 737 Artifact match = matches[i]; 738 if(configStore.containsConfiguration(match) && configManager.isRunning(match)) { 739 return; } 741 } 742 configID = matches[matches.length-1]; 744 } 745 if(configStore.containsConfiguration(configID)) { 746 if(configManager.isRunning(configID)) { 747 return; } 749 data = configStore.loadConfiguration(configID); 750 } 751 Dependency[] dependencies = data == null ? getDependencies(writeableRepo, configID) : getDependencies(data); 752 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 ("Installed configuration into repository but ConfigStore does not see it: "+e.getMessage()); 760 } catch (InvalidConfigException e) { 761 throw new IllegalStateException ("Installed configuration into repository but ConfigStore cannot load it: "+e.getMessage()); 762 } 763 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 { 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 set; 777 try { 778 set = configStore.resolve(configID, null, data.getSourceFile()); 779 } catch (NoSuchConfigException e) { 780 throw new IllegalStateException ("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 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 it = set.iterator(); it.hasNext();) { 796 URL url = (URL ) it.next(); 797 String path = url.getPath(); 798 if(path.lastIndexOf('/') > -1) { 799 path = path.substring(path.lastIndexOf('/')); 800 } 801 File target = new File (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 (target)); 813 } 814 } 815 } 816 817 private void copyFile(InputStream in, FileOutputStream out) throws IOException { 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 832 private File downloadFile(OpenResult result, ResultsFileWriteMonitor monitor) throws IOException { 833 InputStream in = result.getStream(); 834 if(in == null) { 835 throw new IllegalStateException (); 836 } 837 FileOutputStream out = null; 838 try { 839 monitor.writeStarted(result.getConfigID().toString(), result.fileSize); 840 File file = File.createTempFile("geronimo-plugin-download-", ".tmp"); 841 out = new FileOutputStream (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 ignored) { } 859 } 860 if (out != null) { 861 try { 862 out.close(); 863 } catch (IOException ignored) { } 864 } 865 } 866 } 867 868 871 private static Dependency[] getDependencies(Repository repo, Artifact artifact) { 872 Set set = repo.getDependencies(artifact); 873 Dependency[] results = new Dependency[set.size()]; 874 int index=0; 875 for (Iterator 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 885 private static Dependency[] getDependencies(ConfigurationData data) { 886 List dependencies = new ArrayList (data.getEnvironment().getDependencies()); 887 Collection children = data.getChildConfigurations().values(); 888 for (Iterator 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 898 private static URL getURL(Artifact configId, URL repository) throws MalformedURLException { 899 URL context; 900 if(repository.toString().endsWith("/")) { 901 context = repository; 902 } else { 903 context = new URL (repository.toString()+"/"); 904 } 905 906 String qualifiedVersion = configId.getVersion().toString(); 907 if (configId.getVersion() instanceof SnapshotVersion) { 908 SnapshotVersion ssVersion = (SnapshotVersion)configId.getVersion(); 909 String 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 (context, configId.getGroupId().replace('.','/') + "/" 916 + configId.getArtifactId() + "/" + configId.getVersion() 917 + "/" +configId.getArtifactId() + "-" 918 + qualifiedVersion + "." +configId.getType()); 919 } 920 921 939 private static OpenResult openStream(Artifact artifact, URL [] repos, String username, String password, ResultsFileWriteMonitor monitor) throws IOException , FailedLoginException , 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); } 950 InputStream in; 951 LinkedList list = new LinkedList (); 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); } 960 URL repository = (URL ) list.removeFirst(); 961 URL 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 973 private static InputStream connect(URL url, String username, String password, ResultsFileWriteMonitor monitor) throws IOException , FailedLoginException { 974 return connect(url, username, password, monitor, null); 975 } 976 977 981 private static InputStream connect(URL url, String username, String password, ResultsFileWriteMonitor monitor, String method) throws IOException , FailedLoginException { 982 URLConnection con = url.openConnection(); 983 if(con instanceof HttpURLConnection ) { 984 HttpURLConnection http = (HttpURLConnection ) url.openConnection(); 985 if(method != null) { 986 http.setRequestMethod(method); 987 } 988 http.connect(); 989 if(http.getResponseCode() == 401) { if(username == null || username.equals("")) { 991 throw new FailedLoginException ("Server returned 401 "+http.getResponseMessage()); 992 } 993 http = (HttpURLConnection ) url.openConnection(); 994 http.setRequestProperty("Authorization", "Basic " + new String (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 ("Server returned 401 "+http.getResponseMessage()); 1001 } else if(http.getResponseCode() == 404) { 1002 return null; } 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; } 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 (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 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 e) { 1036 return null; 1037 } 1038 } 1039 } 1040 } 1041 1042 1046 private static Artifact findArtifact(Artifact query, URL [] repos, String username, String 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 list = new ArrayList (); 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 url = (URL ) list.get(i); 1057 try { 1058 result = findArtifact(query, url, username, password, monitor); 1059 } catch (Exception 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 1073 private static Artifact findArtifact(Artifact query, URL url, String username, String password, ResultsFileWriteMonitor monitor) throws IOException , FailedLoginException , ParserConfigurationException , SAXException { 1074 monitor.getResults().setCurrentMessage("Searching for "+query+" at "+url); 1075 String base = query.getGroupId().replace('.', '/') + "/" + query.getArtifactId(); 1076 String path = base +"/maven-metadata.xml"; 1077 URL metaURL = new URL (url.toString().endsWith("/") ? url : new URL (url.toString()+"/"), path); 1078 InputStream in = connect(metaURL, username, password, monitor); 1079 if(in == null) { 1080 return null; 1081 } 1082 DocumentBuilder builder = XmlUtil.newDocumentBuilderFactory().newDocumentBuilder(); 1084 Document doc = builder.parse(in); 1085 Element root = doc.getDocumentElement(); 1086 NodeList list = root.getElementsByTagName("versions"); 1087 if(list.getLength() == 0) { 1088 return null; 1089 } 1090 list = ((Element )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 metadataURL = new URL (url.toString()+base+"/"+version+"/maven-metadata.xml"); 1099 InputStream metadataStream = connect(metadataURL, username, password, monitor); 1100 1101 if (metadataStream != null) { 1103 DocumentBuilder metadatabuilder = XmlUtil.newDocumentBuilderFactory().newDocumentBuilder(); 1104 Document metadatadoc = metadatabuilder.parse(metadataStream); 1105 NodeList snapshots = metadatadoc.getDocumentElement().getElementsByTagName("snapshot"); 1106 if (snapshots.getLength() >= 1) { 1107 Element snapshot = (Element )snapshots.item(0); 1108 String [] timestamp = getChildrenText(snapshot, "timestamp"); 1109 String [] 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 nfe) { 1117 log.warn("Could not create snapshot version for " + query); 1118 } 1119 } 1120 } 1121 metadataStream.close(); 1122 } 1123 1124 Artifact verifiedArtifact = new Artifact(query.getGroupId(), query.getArtifactId(), version, query.getType()); 1126 URL test = getURL(verifiedArtifact, url); 1127 InputStream 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 1145 private void readNameAndID(File xml, Map plugins) { 1146 try { 1147 SAXParserFactory factory = XmlUtil.newSAXParserFactory(); 1148 SAXParser 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 e) { 1155 log.warn("Invalid XML at "+xml.getAbsolutePath(), e); 1156 } 1157 } 1158 1159 1166 private void readNameAndID(InputStream xml, Map plugins) { 1167 try { 1168 SAXParserFactory factory = XmlUtil.newSAXParserFactory(); 1169 SAXParser 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 e) { 1176 log.warn("Invalid XML", e); 1177 } 1178 } 1179 1180 1184 private void overrideDependencies(ConfigurationData data, PluginMetadata metadata) { 1185 PluginMetadata temp = createDefaultMetadata(data); 1187 metadata.setDependencies(temp.getDependencies()); 1188 } 1189 1190 1194 private PluginMetadata createDefaultMetadata(ConfigurationData data) { 1195 PluginMetadata meta = new PluginMetadata(data.getId().toString(), data.getId(), "Unknown", "Please provide a description", 1199 null, null, null, true, false); 1204 meta.setGeronimoVersions(new String []{serverInfo.getVersion()}); 1205 meta.setJvmVersions(new String [0]); 1206 meta.setLicenses(new PluginMetadata.License[0]); 1207 meta.setObsoletes(new String []{new Artifact(data.getId().getGroupId(), data.getId().getArtifactId(), (Version)null, data.getId().getType()).toString()}); 1208 meta.setFilesToCopy(new PluginMetadata.CopyFile[0]); 1209 List deps = new ArrayList (); 1210 PluginMetadata.Prerequisite prereq = null; 1211 prereq = processDependencyList(data.getEnvironment().getDependencies(), prereq, deps); 1212 Map children = data.getChildConfigurations(); 1213 for (Iterator 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 []) deps.toArray(new String [deps.size()])); 1218 meta.setPrerequisites(prereq == null ? new PluginMetadata.Prerequisite[0] : new PluginMetadata.Prerequisite[]{prereq}); 1219 return meta; 1220 } 1221 1222 1225 private PluginMetadata loadCARFile(File file, boolean definitelyCAR) throws IOException , ParserConfigurationException , SAXException { 1226 if(!file.canRead()) { 1227 log.error("Cannot read from downloaded CAR file "+file.getAbsolutePath()); 1228 return null; 1229 } 1230 JarFile jar = new JarFile (file); 1231 Document doc; 1232 try { 1233 JarEntry 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 in = jar.getInputStream(entry); 1242 DocumentBuilder 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 1254 private PluginMetadata loadPluginMetadata(Document doc, String file) throws SAXException , MalformedURLException { 1255 Element 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 1267 private PluginList loadPluginList(URL repo, InputStream in) throws ParserConfigurationException , IOException , SAXException { 1268 DocumentBuilder builder = createDocumentBuilder(); 1269 Document doc = builder.parse(in); 1270 in.close(); 1271 Element root = doc.getDocumentElement(); NodeList configs = root.getElementsByTagName("plugin"); 1273 List results = new ArrayList (); 1274 for (int i = 0; i < configs.getLength(); i++) { 1275 Element config = (Element ) configs.item(i); 1276 PluginMetadata data = processPlugin(config); 1277 results.add(data); 1278 } 1279 String [] repos = getChildrenText(root, "default-repository"); 1280 URL [] repoURLs = new URL [repos.length]; 1281 for(int i = 0; i < repos.length; i++) { 1282 if(repos[i].endsWith("/")) { 1283 repoURLs[i] = new URL (repos[i]); 1284 } else { 1285 repoURLs[i] = new URL (repos[i]+"/"); 1286 } 1287 } 1288 1289 PluginMetadata[] data = (PluginMetadata[]) results.toArray(new PluginMetadata[results.size()]); 1290 return new PluginList(repoURLs, data); 1291 } 1292 1293 1298 private static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 1299 DocumentBuilderFactory 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 []{ 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 builder = factory.newDocumentBuilder(); 1311 builder.setErrorHandler(new ErrorHandler () { 1312 public void error(SAXParseException exception) throws SAXException { 1313 throw new SAXException ("Unable to read plugin file", exception); 1314 } 1315 1316 public void fatalError(SAXParseException exception) throws SAXException { 1317 throw new SAXException ("Unable to read plugin file", exception); 1318 } 1319 1320 public void warning(SAXParseException exception) { 1321 log.warn("Warning reading XML document", exception); 1322 } 1323 }); 1324 return builder; 1325 } 1326 1327 1331 private PluginMetadata processPlugin(Element plugin) throws SAXException , MalformedURLException { 1332 String moduleId = getChildText(plugin, "module-id"); 1333 NodeList licenseNodes = plugin.getElementsByTagName("license"); 1334 PluginMetadata.License[] licenses = new PluginMetadata.License[licenseNodes.getLength()]; 1335 for(int j=0; j<licenseNodes.getLength(); j++) { 1336 Element node = (Element ) licenseNodes.item(j); 1337 String licenseName = getText(node); 1338 String openSource = node.getAttribute("osi-approved"); 1339 if(licenseName == null || licenseName.equals("") || openSource == null || openSource.equals("")) { 1340 throw new SAXException ("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 hashList = plugin.getElementsByTagName("hash"); 1346 if(hashList.getLength() > 0) { 1347 Element elem = (Element ) hashList.item(0); 1348 hash = new PluginMetadata.Hash(elem.getAttribute("type"), getText(elem)); 1349 } 1350 NodeList 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 node = (Element ) fileList.item(i); 1354 String relative = node.getAttribute("relative-to"); 1355 String destDir = node.getAttribute("dest-dir"); 1356 String fileName = getText(node); 1357 files[i] = new PluginMetadata.CopyFile(relative.equals("server"), fileName, destDir); 1358 } 1359 NodeList gbeans = plugin.getElementsByTagName("gbean"); 1360 GBeanOverride[] overrides = new GBeanOverride[gbeans.getLength()]; 1361 for (int i = 0; i < overrides.length; i++) { 1362 Element node = (Element ) 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 preNodes = plugin.getElementsByTagName("prerequisite"); 1371 PluginMetadata.Prerequisite[] prereqs = new PluginMetadata.Prerequisite[preNodes.getLength()]; 1372 for(int j=0; j<preNodes.getLength(); j++) { 1373 Element node = (Element ) preNodes.item(j); 1374 String originalConfigId = getChildText(node, "id"); 1375 if(originalConfigId == null) { 1376 throw new SAXException ("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 [] gerVersions = getChildrenText(plugin, "geronimo-version"); 1388 if(gerVersions.length > 0) { 1389 boolean match = checkGeronimoVersions(gerVersions); 1390 if(!match) eligible = false; 1391 } 1392 String [] jvmVersions = getChildrenText(plugin, "jvm-version"); 1393 if(jvmVersions.length > 0) { 1394 boolean match = checkJVMVersions(jvmVersions); 1395 if(!match) eligible = false; 1396 } 1397 String [] repoNames = getChildrenText(plugin, "source-repository"); 1398 URL [] repos = new URL [repoNames.length]; 1399 for (int i = 0; i < repos.length; i++) { 1400 repos[i] = new URL (repoNames[i]); 1401 } 1402 Artifact artifact = null; 1403 boolean installed = false; 1404 if (moduleId != null) { 1405 artifact = Artifact.create(moduleId); 1406 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 list = plugin.getElementsByTagName("dependency"); 1426 List start = new ArrayList (); 1427 String deps[] = new String [list.getLength()]; 1428 for(int i=0; i<list.getLength(); i++) { 1429 Element node = (Element ) 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 []) start.toArray(new String [start.size()])); 1437 data.setObsoletes(getChildrenText(plugin, "obsoletes")); 1438 return data; 1439 } 1440 1441 1448 private boolean checkJVMVersions(String [] jvmVersions) { 1449 if(jvmVersions.length == 0) return true; 1450 String version = System.getProperty("java.version"); 1451 boolean match = false; 1452 for (int j = 0; j < jvmVersions.length; j++) { 1453 String jvmVersion = jvmVersions[j]; 1454 if(jvmVersion == null || jvmVersion.equals("")) { 1455 throw new IllegalStateException ("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 1472 private boolean checkGeronimoVersions(String [] gerVersions) { 1473 if(gerVersions.length == 0) return true; 1474 String version = serverInfo.getVersion(); 1475 boolean match = false; 1476 for (int j = 0; j < gerVersions.length; j++) { 1477 String gerVersion = gerVersions[j]; 1478 if(gerVersion == null || gerVersion.equals("")) { 1479 throw new IllegalStateException ("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 1495 private static String getChildText(Element root, String property) { 1496 NodeList children = root.getChildNodes(); 1497 for(int i=0; i<children.getLength(); i++) { 1498 Node 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 1509 private static String getText(Node target) { 1510 NodeList nodes = target.getChildNodes(); 1511 StringBuffer buf = null; 1512 for(int j=0; j<nodes.getLength(); j++) { 1513 Node node = nodes.item(j); 1514 if(node.getNodeType() == Node.TEXT_NODE) { 1515 if(buf == null) { 1516 buf = new StringBuffer (); 1517 } 1518 buf.append(node.getNodeValue()); 1519 } 1520 } 1521 return buf == null ? null : buf.toString(); 1522 } 1523 1524 1532 private static String [] getChildrenText(Element root, String property) { 1533 NodeList children = root.getChildNodes(); 1534 List results = new ArrayList (); 1535 for(int i=0; i<children.getLength(); i++) { 1536 Node check = children.item(i); 1537 if(check.getNodeType() == Node.ELEMENT_NODE && check.getNodeName().equals(property)) { 1538 NodeList nodes = check.getChildNodes(); 1539 StringBuffer buf = null; 1540 for(int j=0; j<nodes.getLength(); j++) { 1541 Node node = nodes.item(j); 1542 if(node.getNodeType() == Node.TEXT_NODE) { 1543 if(buf == null) { 1544 buf = new StringBuffer (); 1545 } 1546 buf.append(node.getNodeValue()); 1547 } 1548 } 1549 results.add(buf == null ? null : buf.toString()); 1550 } 1551 } 1552 return (String []) results.toArray(new String [results.size()]); 1553 } 1554 1555 1565 private PluginMetadata.Prerequisite processDependencyList(List real, PluginMetadata.Prerequisite prereq, List 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 1591 private static Document writePluginMetadata(PluginMetadata data) throws ParserConfigurationException { 1592 DocumentBuilder builder = createDocumentBuilder(); 1593 Document doc = builder.newDocument(); 1594 Element 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 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 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 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 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 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 content = doc.createElement("config-xml-content"); 1659 for (int i = 0; i < data.getConfigXmls().length; i++) { 1660 GBeanOverride override = data.getConfigXmls()[i]; 1661 Element 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 1676 private static void addTextChild(Document doc, Element parent, String name, String text) { 1677 Element child = doc.createElement(name); 1678 child.appendChild(doc.createTextNode(text)); 1679 parent.appendChild(child); 1680 } 1681 1682 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 1697 private static Object getNextKey() { 1698 int value; 1699 synchronized(PluginInstallerGBean.class) { 1700 value = ++counter; 1701 } 1702 return new Integer (value); 1703 } 1704 1705 1708 private static class PluginNameIDHandler extends DefaultHandler { 1709 private String id = ""; 1710 private String name = ""; 1711 private String element = null; 1712 1713 public void characters(char ch[], int start, int length) throws SAXException { 1714 if(element != null) { 1715 if(element.equals("module-id")) { 1716 id += new String (ch, start, length); 1717 } else if(element.equals("name")) { 1718 name += new String (ch, start, length); 1719 } 1720 } 1721 } 1722 1723 public void endElement(String uri, String localName, String qName) throws SAXException { 1724 element = null; 1725 } 1726 1727 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 1728 if(qName.equals("module-id") || qName.equals("name")) { 1729 element = qName; 1730 } 1731 } 1732 1733 public void endDocument() throws SAXException { 1734 id = id.trim(); 1735 name = name.trim(); 1736 } 1737 1738 public String getID() { 1739 return id; 1740 } 1741 1742 public String getName() { 1743 return name; 1744 } 1745 1746 public boolean isComplete() { 1747 return !id.equals("") && !name.equals(""); 1748 } 1749 } 1750 1751 1754 private static class ResultsFileWriteMonitor implements FileWriteMonitor { 1755 private final DownloadPoller results; 1756 private int totalBytes; 1757 private String 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 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 1801 private static class OpenResult { 1802 private final InputStream stream; 1803 private final Artifact configID; 1804 private final int fileSize; 1805 1806 public OpenResult(Artifact configID, InputStream 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 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 []{"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 |