001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.system.plugin; 018 019 import java.io.BufferedOutputStream; 020 import java.io.File; 021 import java.io.FileOutputStream; 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.net.URI; 025 import java.net.URL; 026 import java.util.ArrayList; 027 import java.util.Collection; 028 import java.util.Collections; 029 import java.util.Comparator; 030 import java.util.Enumeration; 031 import java.util.HashMap; 032 import java.util.HashSet; 033 import java.util.Iterator; 034 import java.util.List; 035 import java.util.Map; 036 import java.util.Properties; 037 import java.util.Set; 038 import java.util.SortedSet; 039 import java.util.Stack; 040 import java.util.TreeSet; 041 import java.util.jar.JarEntry; 042 import java.util.jar.JarFile; 043 import java.util.jar.JarOutputStream; 044 import java.util.jar.Manifest; 045 import java.util.regex.Matcher; 046 import java.util.regex.Pattern; 047 import java.util.zip.ZipEntry; 048 049 import javax.security.auth.login.FailedLoginException; 050 import javax.xml.parsers.SAXParser; 051 import javax.xml.parsers.SAXParserFactory; 052 053 import org.apache.commons.logging.Log; 054 import org.apache.commons.logging.LogFactory; 055 import org.apache.geronimo.gbean.GBeanInfo; 056 import org.apache.geronimo.gbean.GBeanInfoBuilder; 057 import org.apache.geronimo.gbean.ReferenceCollection; 058 import org.apache.geronimo.gbean.ReferenceCollectionEvent; 059 import org.apache.geronimo.gbean.ReferenceCollectionListener; 060 import org.apache.geronimo.kernel.InvalidGBeanException; 061 import org.apache.geronimo.kernel.Kernel; 062 import org.apache.geronimo.kernel.basic.BasicKernel; 063 import org.apache.geronimo.kernel.config.ConfigurationData; 064 import org.apache.geronimo.kernel.config.ConfigurationManager; 065 import org.apache.geronimo.kernel.config.ConfigurationStore; 066 import org.apache.geronimo.kernel.config.InvalidConfigException; 067 import org.apache.geronimo.kernel.config.KernelConfigurationManager; 068 import org.apache.geronimo.kernel.config.NoSuchConfigException; 069 import org.apache.geronimo.kernel.config.NoSuchStoreException; 070 import org.apache.geronimo.kernel.config.PersistentConfigurationList; 071 import org.apache.geronimo.kernel.repository.Artifact; 072 import org.apache.geronimo.kernel.repository.ArtifactManager; 073 import org.apache.geronimo.kernel.repository.DefaultArtifactManager; 074 import org.apache.geronimo.kernel.repository.Dependency; 075 import org.apache.geronimo.kernel.repository.FileWriteMonitor; 076 import org.apache.geronimo.kernel.repository.ImportType; 077 import org.apache.geronimo.kernel.repository.MissingDependencyException; 078 import org.apache.geronimo.kernel.repository.Repository; 079 import org.apache.geronimo.kernel.repository.Version; 080 import org.apache.geronimo.kernel.repository.WritableListableRepository; 081 import org.apache.geronimo.kernel.util.XmlUtil; 082 import org.apache.geronimo.system.configuration.ConfigurationStoreUtil; 083 import org.apache.geronimo.system.configuration.RepositoryConfigurationStore; 084 import org.apache.geronimo.system.configuration.PluginAttributeStore; 085 import org.apache.geronimo.system.plugin.model.ArtifactType; 086 import org.apache.geronimo.system.plugin.model.ConfigXmlContentType; 087 import org.apache.geronimo.system.plugin.model.CopyFileType; 088 import org.apache.geronimo.system.plugin.model.DependencyType; 089 import org.apache.geronimo.system.plugin.model.HashType; 090 import org.apache.geronimo.system.plugin.model.LicenseType; 091 import org.apache.geronimo.system.plugin.model.PluginArtifactType; 092 import org.apache.geronimo.system.plugin.model.PluginListType; 093 import org.apache.geronimo.system.plugin.model.PluginType; 094 import org.apache.geronimo.system.plugin.model.PrerequisiteType; 095 import org.apache.geronimo.system.plugin.model.PropertyType; 096 import org.apache.geronimo.system.plugin.model.AttributesType; 097 import org.apache.geronimo.system.plugin.model.ModuleType; 098 import org.apache.geronimo.system.repository.Maven2Repository; 099 import org.apache.geronimo.system.serverinfo.BasicServerInfo; 100 import org.apache.geronimo.system.serverinfo.ServerInfo; 101 import org.apache.geronimo.system.threads.ThreadPool; 102 import org.codehaus.plexus.util.FileUtils; 103 import org.xml.sax.Attributes; 104 import org.xml.sax.SAXException; 105 import org.xml.sax.helpers.DefaultHandler; 106 107 /** 108 * A GBean that knows how to download configurations from a Maven repository. 109 * 110 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 111 */ 112 public class PluginInstallerGBean implements PluginInstaller { 113 private final static Log log = LogFactory.getLog(PluginInstallerGBean.class); 114 115 116 private static int counter; 117 private final ConfigurationManager configManager; 118 private final GeronimoSourceRepository localSourceRepository; 119 private final WritableListableRepository writeableRepo; 120 private final ConfigurationStore configStore; 121 private final ServerInfo serverInfo; 122 private final Map<Object, DownloadResults> asyncKeys; 123 private final ThreadPool threadPool; 124 private final Collection<? extends ServerInstanceData> serverInstanceDatas; 125 private final ClassLoader classLoader; 126 private final Map<String, ServerInstance> servers = new HashMap<String, ServerInstance>(); 127 private final Collection<PersistentConfigurationList> persistentConfigurationLists; 128 129 // This regular expression for repository filename is taken from Maven1Repository.MAVEN_1_PATTERN 130 private static final Pattern MAVEN_1_PATTERN_PART = Pattern.compile("(.+)-([0-9].+)\\.([^0-9]+)"); 131 132 /** 133 * GBean constructor. Supply an existing ConfigurationManager. Use for adding to the current server. 134 * 135 * @param configManager Configuration Manager for this server 136 * @param repository repository to install into 137 * @param configStore configuration store to install into 138 * @param serverInstanceDatas set of server "layouts" to install config info into 139 * @param serverInfo location of server 140 * @param threadPool thread pool for async operations 141 * @param artifactManager artifact manager to resolve existing artifacts 142 * @param persistentConfigurationLists used to start new plugins in a running server 143 * @param classLoader classLoader @throws IOException exception if server instance cannot be loaded 144 * @throws java.io.IOException from bad ServerInstance 145 */ 146 public PluginInstallerGBean(ConfigurationManager configManager, 147 WritableListableRepository repository, 148 ConfigurationStore configStore, 149 Collection<? extends ServerInstanceData> serverInstanceDatas, 150 final ServerInfo serverInfo, 151 ThreadPool threadPool, 152 final ArtifactManager artifactManager, 153 Collection<PersistentConfigurationList> persistentConfigurationLists, 154 final ClassLoader classLoader) throws IOException { 155 this.writeableRepo = repository; 156 this.configStore = configStore; 157 this.serverInfo = serverInfo; 158 this.threadPool = threadPool; 159 asyncKeys = Collections.synchronizedMap(new HashMap<Object, DownloadResults>()); 160 this.serverInstanceDatas = serverInstanceDatas; 161 this.persistentConfigurationLists = persistentConfigurationLists == null ? Collections.<PersistentConfigurationList>emptyList() : persistentConfigurationLists; 162 this.classLoader = classLoader; 163 if (configManager == null) { 164 throw new IllegalArgumentException("No default server instance set up"); 165 } 166 this.configManager = configManager; 167 localSourceRepository = new GeronimoSourceRepository(configManager.getRepositories(), configManager.getArtifactResolver()); 168 setUpServerInstances(serverInstanceDatas, serverInfo, artifactManager, servers, writeableRepo, true); 169 } 170 171 /** 172 * Constructor for use in assembling a new server. 173 * 174 * @param serverInstanceDatas set of server layouts 175 * @param kernel kernel for current server 176 * @param classLoader classLoader 177 * @param targetRepositoryPath location of repo to install into (not in current server) 178 * @param targetServerPath location of server to install into (not current server 179 * @throws IOException if layouts can't be loaded 180 */ 181 public PluginInstallerGBean(String targetRepositoryPath, 182 String targetServerPath, 183 Collection<? extends ServerInstanceData> serverInstanceDatas, 184 final Kernel kernel, 185 final ClassLoader classLoader) throws Exception { 186 final ArtifactManager artifactManager = new DefaultArtifactManager(); 187 188 FileUtils.forceMkdir(new File(targetServerPath)); 189 serverInfo = new BasicServerInfo(targetServerPath, false); 190 File targetRepositoryFile = serverInfo.resolve(targetRepositoryPath); 191 FileUtils.forceMkdir(targetRepositoryFile); 192 writeableRepo = new Maven2Repository(targetRepositoryFile); 193 configStore = new RepositoryConfigurationStore(writeableRepo); 194 threadPool = null; 195 asyncKeys = Collections.synchronizedMap(new HashMap<Object, DownloadResults>()); 196 this.serverInstanceDatas = serverInstanceDatas; 197 this.persistentConfigurationLists = Collections.emptyList(); 198 this.classLoader = classLoader; 199 setUpServerInstances(serverInstanceDatas, serverInfo, artifactManager, servers, writeableRepo, false); 200 this.configManager = buildConfigurationManager(artifactManager, writeableRepo, kernel, configStore, classLoader, servers); 201 localSourceRepository = new GeronimoSourceRepository(configManager.getRepositories(), configManager.getArtifactResolver()); 202 } 203 204 private static void setUpServerInstances(Collection<? extends ServerInstanceData> serverInstanceDatas, 205 final ServerInfo serverInfo, final ArtifactManager artifactManager, 206 final Map<String, ServerInstance> servers, 207 final WritableListableRepository writeableRepo, 208 final boolean live) throws IOException { 209 List<ServerInstanceData> datas = new ArrayList<ServerInstanceData>(serverInstanceDatas); 210 boolean shrank = true; 211 while (shrank) { 212 shrank = false; 213 for (Iterator<ServerInstanceData> it = datas.iterator(); it.hasNext();) { 214 ServerInstanceData instance = it.next(); 215 String dependsOn = instance.getAttributeManagerFrom(); 216 if (dependsOn == null || servers.containsKey(dependsOn)) { 217 addServerInstance(instance, artifactManager, writeableRepo, serverInfo, servers, live); 218 it.remove(); 219 shrank = true; 220 } 221 } 222 } 223 if (!datas.isEmpty()) { 224 throw new IllegalStateException("Cannot resolve ServerInstanceDatas: " + datas); 225 } 226 if (serverInstanceDatas instanceof ReferenceCollection) { 227 ((ReferenceCollection) serverInstanceDatas).addReferenceCollectionListener(new ReferenceCollectionListener() { 228 229 public void memberAdded(ReferenceCollectionEvent event) { 230 ServerInstanceData instance = (ServerInstanceData) event.getMember(); 231 try { 232 addServerInstance(instance, artifactManager, writeableRepo, serverInfo, servers, live); 233 } catch (IOException e) { 234 //nothing to do?? log??? 235 } 236 } 237 238 public void memberRemoved(ReferenceCollectionEvent event) { 239 ServerInstanceData instance = (ServerInstanceData) event.getMember(); 240 servers.remove(instance.getName()); 241 } 242 }); 243 } 244 } 245 246 private static void addServerInstance(ServerInstanceData serverInstance, 247 ArtifactManager artifactManager, 248 WritableListableRepository targetRepo, 249 ServerInfo serverInfo, 250 Map<String, org.apache.geronimo.system.plugin.ServerInstance> servers, 251 boolean live) throws IOException { 252 File targetConfigDirectory = serverInfo.resolveServer(serverInstance.getConfigFile()).getParentFile(); 253 FileUtils.forceMkdir(targetConfigDirectory); 254 org.apache.geronimo.system.plugin.ServerInstance instance = serverInstance.getServerInstance(artifactManager, targetRepo, serverInfo, servers, live); 255 servers.put(instance.getServerName(), instance); 256 } 257 258 private static ConfigurationManager buildConfigurationManager(ArtifactManager artifactManager, 259 WritableListableRepository targetRepo, 260 Kernel kernel, 261 ConfigurationStore targetStore, 262 ClassLoader classloader, 263 Map<String, org.apache.geronimo.system.plugin.ServerInstance> servers) throws IOException { 264 for (ServerInstance instance : servers.values()) { 265 if ("default".equals(instance.getServerName())) { 266 KernelConfigurationManager configurationManager = new KernelConfigurationManager(kernel, 267 Collections.singleton(targetStore), 268 instance.getAttributeStore(), 269 (PersistentConfigurationList) instance.getAttributeStore(), 270 artifactManager, 271 instance.getArtifactResolver(), 272 Collections.singleton(targetRepo), 273 null, 274 classloader); 275 configurationManager.setOnline(false); 276 return configurationManager; 277 } 278 } 279 throw new IllegalStateException("No default server instance found: " + servers.keySet()); 280 } 281 282 /* now for tests only */ 283 PluginInstallerGBean(ConfigurationManager configManager, WritableListableRepository repository, ConfigurationStore configStore, ServerInfo serverInfo, ThreadPool threadPool, Collection<ServerInstance> servers) { 284 this.configManager = configManager; 285 localSourceRepository = new GeronimoSourceRepository(configManager.getRepositories(), configManager.getArtifactResolver()); 286 this.writeableRepo = repository; 287 this.configStore = configStore; 288 this.serverInfo = serverInfo; 289 this.threadPool = threadPool; 290 asyncKeys = Collections.synchronizedMap(new HashMap<Object, DownloadResults>()); 291 serverInstanceDatas = null; 292 this.persistentConfigurationLists = Collections.emptyList(); 293 classLoader = null; 294 for (ServerInstance instance : servers) { 295 this.servers.put(instance.getServerName(), instance); 296 } 297 if (servers instanceof ReferenceCollection) { 298 ((ReferenceCollection) servers).addReferenceCollectionListener(new ReferenceCollectionListener() { 299 300 public void memberAdded(ReferenceCollectionEvent event) { 301 ServerInstance instance = (ServerInstance) event.getMember(); 302 PluginInstallerGBean.this.servers.put(instance.getServerName(), instance); 303 } 304 305 public void memberRemoved(ReferenceCollectionEvent event) { 306 ServerInstance instance = (ServerInstance) event.getMember(); 307 PluginInstallerGBean.this.servers.remove(instance.getServerName()); 308 } 309 }); 310 } 311 } 312 313 /** 314 * This more or less clones the current PluginInstallerGBean to create one with the same server instances (structure) but using 315 * the current server as config store and assembling a server in a provided location. 316 * 317 * @param targetRepositoryPath location of repository in new server (normally "repository") 318 * @param relativeTargetServerPath Location of server to assemble relative to current server 319 * @param pluginList list of plugins to install 320 * @throws Exception if something goes wrong 321 */ 322 public DownloadResults installPluginList(String targetRepositoryPath, String relativeTargetServerPath, PluginListType pluginList) throws Exception { 323 DownloadResults downloadPoller = new DownloadResults(); 324 File targetServerPath = serverInfo.resolveServer(relativeTargetServerPath); 325 if (targetServerPath.exists()) { 326 FileUtils.forceDelete(targetServerPath); 327 } 328 String targetServerPathName = targetServerPath.getAbsolutePath(); 329 Kernel kernel = new BasicKernel("assembly"); 330 331 try { 332 // kernel.boot(); 333 PluginInstallerGBean installer = new PluginInstallerGBean( 334 targetRepositoryPath, 335 targetServerPathName, 336 serverInstanceDatas, 337 kernel, 338 classLoader); 339 340 installer.install(pluginList, localSourceRepository, true, null, null, downloadPoller); 341 } finally { 342 kernel.shutdown(); 343 } 344 return downloadPoller; 345 } 346 347 public void mergeOverrides(String server, AttributesType overrides) throws InvalidGBeanException, IOException { 348 ServerInstance serverInstance = servers.get(server); 349 if (serverInstance == null) { 350 throw new NullPointerException("No such server: " + server + ", known servers: " + servers.keySet()); 351 } 352 PluginAttributeStore attributeStore = serverInstance.getAttributeStore(); 353 for (ModuleType module: overrides.getModule()) { 354 Artifact artifact = Artifact.create(module.getName()); 355 attributeStore.setModuleGBeans(artifact, module.getGbean(), module.isLoad(), module.getCondition()); 356 attributeStore.save(); 357 } 358 if (overrides.getConfiguration().size() > 0) { 359 throw new UnsupportedOperationException("Use modules, not configurations"); 360 } 361 } 362 363 364 /** 365 * Lists the plugins installed in the local Geronimo server, by name and 366 * ID. 367 * 368 * @return A Map with key type String (plugin name) and value type Artifact 369 * (config ID of the plugin). 370 */ 371 public Map getInstalledPlugins() { 372 SortedSet<Artifact> artifacts = writeableRepo.list(); 373 374 Map<String, Artifact> plugins = new HashMap<String, Artifact>(); 375 for (Artifact configId : artifacts) { 376 File dir = writeableRepo.getLocation(configId); 377 if (dir.isDirectory()) { 378 File meta = new File(dir, "META-INF"); 379 if (!meta.isDirectory() || !meta.canRead()) { 380 continue; 381 } 382 File xml = new File(meta, "geronimo-plugin.xml"); 383 if (!xml.isFile() || !xml.canRead() || xml.length() == 0) { 384 continue; 385 } 386 readNameAndID(xml, plugins); 387 } else { 388 if (!dir.isFile() || !dir.canRead()) { 389 log.error("Cannot read artifact dir " + dir.getAbsolutePath()); 390 throw new IllegalStateException("Cannot read artifact dir " + dir.getAbsolutePath()); 391 } 392 try { 393 JarFile jar = new JarFile(dir); 394 try { 395 ZipEntry entry = jar.getEntry("META-INF/geronimo-plugin.xml"); 396 if (entry == null) { 397 continue; 398 } 399 InputStream in = jar.getInputStream(entry); 400 readNameAndID(in, plugins); 401 in.close(); 402 } finally { 403 jar.close(); 404 } 405 } catch (IOException e) { 406 log.error("Unable to read JAR file " + dir.getAbsolutePath(), e); 407 } 408 } 409 } 410 return plugins; 411 } 412 413 /** 414 * Gets a ConfigurationMetadata for a configuration installed in the local 415 * server. Should load a saved one if available, or else create a new 416 * default one to the best of its abilities. 417 * 418 * @param moduleId Identifies the configuration. This must match a 419 * configuration currently installed in the local server. 420 * The configId must be fully resolved (isResolved() == true) 421 */ 422 public PluginType getPluginMetadata(Artifact moduleId) { 423 PluginType type = localSourceRepository.extractPluginMetadata(moduleId); 424 if (null == type) { 425 try { 426 type = createDefaultMetadata(moduleId); 427 } catch (InvalidConfigException e) { 428 log.warn("Unable to generate metadata for " + moduleId, e); 429 } catch (Exception e) { 430 log.warn("Error generating metadata for " + moduleId, e); 431 } 432 } 433 return type; 434 } 435 436 /** 437 * Saves a ConfigurationMetadata for a particular plugin, if the server is 438 * able to record it. This can be used if you later re-export the plugin, 439 * or just want to review the information for a particular installed 440 * plugin. 441 * 442 * @param metadata The data to save. The contained configId (which must 443 * be fully resolved) identifies the configuration to save 444 * this for. 445 */ 446 public void updatePluginMetadata(PluginType metadata) { 447 PluginArtifactType instance = metadata.getPluginArtifact().get(0); 448 Artifact artifact = toArtifact(instance.getModuleId()); 449 File dir = writeableRepo.getLocation(artifact); 450 if (dir == null) { 451 log.error(artifact + " is not installed."); 452 throw new IllegalArgumentException(artifact + " is not installed."); 453 } 454 if (!dir.isDirectory()) { // must be a packed (JAR-formatted) plugin 455 try { 456 File temp = new File(dir.getParentFile(), dir.getName() + ".temp"); 457 JarFile input = new JarFile(dir); 458 Manifest manifest = input.getManifest(); 459 JarOutputStream out = manifest == null ? new JarOutputStream( 460 new BufferedOutputStream(new FileOutputStream(temp))) 461 : new JarOutputStream(new BufferedOutputStream(new FileOutputStream(temp)), manifest); 462 Enumeration en = input.entries(); 463 byte[] buf = new byte[4096]; 464 int count; 465 while (en.hasMoreElements()) { 466 JarEntry entry = (JarEntry) en.nextElement(); 467 if (entry.getName().equals("META-INF/geronimo-plugin.xml")) { 468 entry = new JarEntry(entry.getName()); 469 out.putNextEntry(entry); 470 PluginXmlUtil.writePluginMetadata(metadata, out); 471 } else if (entry.getName().equals("META-INF/MANIFEST.MF")) { 472 // do nothing, already passed in a manifest 473 } else { 474 out.putNextEntry(entry); 475 InputStream in = input.getInputStream(entry); 476 while ((count = in.read(buf)) > -1) { 477 out.write(buf, 0, count); 478 } 479 in.close(); 480 out.closeEntry(); 481 } 482 } 483 out.flush(); 484 out.close(); 485 input.close(); 486 if (!dir.delete()) { 487 log.error("Unable to delete old plugin at " + dir.getAbsolutePath()); 488 throw new IOException("Unable to delete old plugin at " + dir.getAbsolutePath()); 489 } 490 if (!temp.renameTo(dir)) { 491 log.error("Unable to move new plugin " + temp.getAbsolutePath() + " to " + dir.getAbsolutePath()); 492 throw new IOException( 493 "Unable to move new plugin " + temp.getAbsolutePath() + " to " + dir.getAbsolutePath()); 494 } 495 } catch (Exception e) { 496 log.error("Unable to update plugin metadata", e); 497 throw new RuntimeException("Unable to update plugin metadata", e); 498 } // TODO this really should have a finally block to ensure streams are closed 499 } else { 500 File meta = new File(dir, "META-INF"); 501 if (!meta.isDirectory() || !meta.canRead()) { 502 log.error(artifact + " is not a plugin."); 503 throw new IllegalArgumentException(artifact + " is not a plugin."); 504 } 505 File xml = new File(meta, "geronimo-plugin.xml"); 506 FileOutputStream fos = null; 507 try { 508 if (!xml.isFile()) { 509 if (!xml.createNewFile()) { 510 log.error("Cannot create plugin metadata file for " + artifact); 511 throw new RuntimeException("Cannot create plugin metadata file for " + artifact); 512 } 513 } 514 fos = new FileOutputStream(xml); 515 PluginXmlUtil.writePluginMetadata(metadata, fos); 516 } catch (Exception e) { 517 log.error("Unable to save plugin metadata for " + artifact, e); 518 } finally { 519 if (fos != null) { 520 try { 521 fos.close(); 522 } catch (IOException ignored) { 523 // ignored 524 } 525 } 526 } 527 } 528 } 529 530 /** 531 * Lists the plugins available for download in a particular Geronimo repository. 532 * 533 * @param mavenRepository The base URL to the maven repository. This must 534 * contain the file geronimo-plugins.xml 535 * @param username Optional username, if the maven repo uses HTTP Basic authentication. 536 * Set this to null if no authentication is required. 537 * @param password Optional password, if the maven repo uses HTTP Basic authentication. 538 * Set this to null if no authentication is required. 539 */ 540 public PluginListType listPlugins(URL mavenRepository, String username, String password) throws IOException, FailedLoginException { 541 try { 542 SourceRepository repo = SourceRepositoryFactory.getSourceRepository(mavenRepository.toString(), username, password); 543 return repo.getPluginList(); 544 } catch (IllegalStateException e) { 545 return null; 546 } 547 } 548 549 private SourceRepository getDefaultSourceRepository(String defaultRepository, 550 boolean restrictToDefaultRepository) { 551 if (restrictToDefaultRepository && defaultRepository == null) { 552 throw new IllegalArgumentException("You must supply a default repository if you want to restrict to it"); 553 } 554 SourceRepository defaultSourceRepository = defaultRepository == null ? null : SourceRepositoryFactory.getSourceRepository(defaultRepository); 555 return defaultSourceRepository; 556 } 557 558 /** 559 * Installs a configuration from a remote repository into the local Geronimo server, 560 * including all its dependencies. The caller will get the results when the 561 * operation completes. Note that this method does not throw exceptions on failure, 562 * but instead sets the failure property of the DownloadResults. 563 * 564 * @param pluginsToInstall The list of configurations to install 565 * @param defaultRepository Default repo to look for plugins in 566 * @param restrictToDefaultRepository Whether to follow hints to other plugin repos. 567 * @param username Optional username, if the maven repo uses HTTP Basic authentication. 568 * Set this to null if no authentication is required. 569 * @param password Optional password, if the maven repo uses HTTP Basic authentication. 570 * Set this to null if no authentication is required. 571 */ 572 public DownloadResults install(PluginListType pluginsToInstall, String defaultRepository, boolean restrictToDefaultRepository, String username, String password) { 573 DownloadResults results = new DownloadResults(); 574 install(pluginsToInstall, defaultRepository, restrictToDefaultRepository, username, password, results); 575 return results; 576 } 577 578 /** 579 * Installs a configuration from a remote repository into the local Geronimo server, 580 * including all its dependencies. The method blocks until the operation completes, 581 * but the caller will be notified of progress frequently along the way (using the 582 * supplied DownloadPoller). Therefore the caller is meant to create the poller and 583 * then call this method in a background thread. Note that this method does not 584 * throw exceptions on failure, but instead sets the failure property of the 585 * DownloadPoller. 586 * 587 * @param pluginsToInstall The list of configurations to install 588 * @param defaultRepository Default repo to look for plugins in (not required) 589 * @param restrictToDefaultRepository Whether to follow hints to other plugin repos. 590 * @param username Optional username, if the maven repo uses HTTP Basic authentication. 591 * Set this to null if no authentication is required. 592 * @param password Optional password, if the maven repo uses HTTP Basic authentication. 593 * Set this to null if no authentication is required. 594 * @param poller Will be notified with status updates as the download proceeds 595 */ 596 public void install(PluginListType pluginsToInstall, String defaultRepository, boolean restrictToDefaultRepository, String username, String password, DownloadPoller poller) { 597 SourceRepository defaultSourceRepository = getDefaultSourceRepository(defaultRepository, restrictToDefaultRepository); 598 install(pluginsToInstall, defaultSourceRepository, restrictToDefaultRepository, username, password, poller); 599 } 600 601 public void install(PluginListType pluginsToInstall, SourceRepository defaultRepository, boolean restrictToDefaultRepository, String username, String password, DownloadPoller poller) { 602 install(pluginsToInstall, defaultRepository, restrictToDefaultRepository, username, password, poller, true); 603 } 604 605 public void install(PluginListType pluginsToInstall, SourceRepository defaultRepository, boolean restrictToDefaultRepository, String username, String password, DownloadPoller poller, boolean validatePlugins) { 606 List<Artifact> downloadedArtifacts = new ArrayList<Artifact>(); 607 try { 608 Map<Artifact, PluginType> metaMap = new HashMap<Artifact, PluginType>(); 609 // Step 1: validate everything 610 List<PluginType> toInstall = new ArrayList<PluginType>(); 611 for (PluginType metadata : pluginsToInstall.getPlugin()) { 612 try { 613 if (validatePlugins) { 614 validatePlugin(metadata); 615 verifyPrerequisites(metadata); 616 } 617 618 PluginArtifactType instance = metadata.getPluginArtifact().get(0); 619 620 if (instance.getModuleId() != null) { 621 metaMap.put(toArtifact(instance.getModuleId()), metadata); 622 } 623 toInstall.add(metadata); 624 } catch (MissingDependencyException e) { 625 poller.addSkippedConfigID(e); 626 } 627 } 628 629 // Step 2: everything is valid, do the installation 630 for (PluginType metadata : toInstall) { 631 // 2. Unload obsoleted configurations 632 PluginArtifactType instance = metadata.getPluginArtifact().get(0); 633 List<Artifact> obsoletes = new ArrayList<Artifact>(); 634 for (ArtifactType obs : instance.getObsoletes()) { 635 Artifact obsolete = toArtifact(obs); 636 Artifact[] list = configManager.getArtifactResolver().queryArtifacts(obsolete); 637 for (Artifact artifact : list) { 638 if (configManager.isLoaded(artifact)) { 639 if (configManager.isRunning(artifact)) { 640 configManager.stopConfiguration(artifact); 641 } 642 configManager.unloadConfiguration(artifact); 643 obsoletes.add(artifact); 644 } 645 } 646 } 647 // 3. Download the artifact if necessary, and its dependencies 648 Set<Artifact> working = new HashSet<Artifact>(); 649 Stack<Artifact> parentStack = new Stack<Artifact>(); 650 if (instance.getModuleId() != null) { 651 Artifact entry = toArtifact(instance.getModuleId()); 652 List<SourceRepository> repos = getRepos(pluginsToInstall, defaultRepository, restrictToDefaultRepository, instance); 653 downloadArtifact(entry, metaMap, repos, 654 username, password, new ResultsFileWriteMonitor(poller), working, parentStack, false, servers, true); 655 downloadedArtifacts.add(entry); 656 } else { 657 List<DependencyType> deps = instance.getDependency(); 658 for (DependencyType dep : deps) { 659 Artifact entry = toArtifact(dep); 660 List<SourceRepository> repos = getRepos(pluginsToInstall, defaultRepository, restrictToDefaultRepository, instance); 661 downloadArtifact(entry, metaMap, repos, 662 username, password, new ResultsFileWriteMonitor(poller), working, parentStack, false, servers, dep.isStart()); 663 downloadedArtifacts.add(entry); 664 } 665 } 666 // 4. Uninstall obsolete configurations 667 for (Artifact artifact : obsoletes) { 668 configManager.uninstallConfiguration(artifact); 669 } 670 // 5. Installation of this configuration finished successfully 671 } 672 673 // Step 3: Start anything that's marked accordingly 674 if (configManager.isOnline()) { 675 poller.setCurrentFilePercent(-1); 676 for (PersistentConfigurationList persistentConfigurationList : persistentConfigurationLists) { 677 List<Artifact> artifacts = persistentConfigurationList.restore(); 678 for (Artifact artifact : artifacts) { 679 if (!configManager.isRunning(artifact)) { 680 poller.setCurrentMessage("Starting " + artifact); 681 if (!configManager.isLoaded(artifact)) { 682 configManager.loadConfiguration(artifact); 683 } 684 configManager.startConfiguration(artifact); 685 } 686 } 687 } 688 } 689 //ensure config.xml is saved. 690 for (org.apache.geronimo.system.plugin.ServerInstance serverInstance : servers.values()) { 691 serverInstance.getAttributeStore().save(); 692 } 693 } catch (Exception e) { 694 log.error("Unable to install plugin. ", e); 695 poller.setFailure(e); 696 //Attempt to cleanup a failed plugin installation 697 for (Artifact artifact : downloadedArtifacts) { 698 try { 699 configManager.uninstallConfiguration(artifact); 700 } catch (Exception e2) { 701 log.warn("Warning: ",e2); 702 } 703 } 704 } finally { 705 poller.setFinished(); 706 } 707 } 708 709 private List<SourceRepository> getRepos(PluginListType pluginsToInstall, SourceRepository defaultRepository, boolean restrictToDefaultRepository, PluginArtifactType instance) { 710 List<SourceRepository> repos = new ArrayList<SourceRepository>(); 711 if (defaultRepository != null) { 712 repos.add(defaultRepository); 713 } 714 if (!restrictToDefaultRepository) { 715 List<String> repoLocations; 716 if (!instance.getSourceRepository().isEmpty()) { 717 repoLocations = instance.getSourceRepository(); 718 } else { 719 repoLocations = pluginsToInstall.getDefaultRepository(); 720 } 721 for (String repoLocation : repoLocations) { 722 SourceRepository repo = SourceRepositoryFactory.getSourceRepository(repoLocation); 723 repos.add(repo); 724 } 725 } 726 return repos; 727 } 728 729 /** 730 * Installs a configuration from a remote repository into the local Geronimo server, 731 * including all its dependencies. The method returns immediately, providing a key 732 * that can be used to poll the status of the download operation. Note that the 733 * installation does not throw exceptions on failure, but instead sets the failure 734 * property of the DownloadResults that the caller can poll for. 735 * 736 * @param pluginsToInstall The list of configurations to install 737 * @param defaultRepository Default repo to look for plugins in 738 * @param restrictToDefaultRepository Whether to follow hints to other plugin repos. 739 * @param username Optional username, if the maven repo uses HTTP Basic authentication. 740 * Set this to null if no authentication is required. 741 * @param password Optional password, if the maven repo uses HTTP Basic authentication. 742 * Set this to null if no authentication is required. @return A key that can be passed to checkOnInstall 743 */ 744 public Object startInstall(final PluginListType pluginsToInstall, final String defaultRepository, final boolean restrictToDefaultRepository, final String username, final String password) { 745 Object key = getNextKey(); 746 final DownloadResults results = new DownloadResults(); 747 Runnable work = new Runnable() { 748 public void run() { 749 install(pluginsToInstall, defaultRepository, restrictToDefaultRepository, username, password, results); 750 } 751 }; 752 asyncKeys.put(key, results); 753 try { 754 threadPool.execute("Configuration Installer", work); 755 } catch (InterruptedException e) { 756 log.error("Unable to start work", e); 757 throw new RuntimeException("Unable to start work", e); 758 } 759 return key; 760 } 761 762 /** 763 * Installs a configuration downloaded from a remote repository into the local Geronimo 764 * server, including all its dependencies. The method returns immediately, providing a 765 * key that can be used to poll the status of the download operation. Note that the 766 * installation does not throw exceptions on failure, but instead sets the failure 767 * property of the DownloadResults that the caller can poll for. 768 * 769 * @param carFile A CAR file downloaded from a remote repository. This is a packaged 770 * configuration with included configuration information, but it may 771 * still have external dependencies that need to be downloaded 772 * separately. The metadata in the CAR file includes a repository URL 773 * for these downloads, and the username and password arguments are 774 * used in conjunction with that. 775 * @param defaultRepository Default repo to look for plugins in 776 * @param restrictToDefaultRepository Whether to follow hints to other plugin repos. 777 * @param username Optional username, if the maven repo uses HTTP Basic authentication. 778 * Set this to null if no authentication is required. 779 * @param password Optional password, if the maven repo uses HTTP Basic authentication. 780 * Set this to null if no authentication is required. @return A key that can be passed to checkOnInstall 781 */ 782 public Object startInstall(final File carFile, final String defaultRepository, final boolean restrictToDefaultRepository, final String username, final String password) { 783 Object key = getNextKey(); 784 final DownloadResults results = new DownloadResults(); 785 Runnable work = new Runnable() { 786 public void run() { 787 install(carFile, defaultRepository, restrictToDefaultRepository, username, password, results); 788 } 789 }; 790 asyncKeys.put(key, results); 791 try { 792 threadPool.execute("Configuration Installer", work); 793 } catch (InterruptedException e) { 794 log.error("Unable to start work", e); 795 throw new RuntimeException("Unable to start work", e); 796 } 797 return key; 798 } 799 800 /** 801 * Gets the current progress of a download operation. Note that once the 802 * DownloadResults is returned for this operation shows isFinished = true, 803 * the operation will be forgotten, so the caller should be careful not to 804 * call this again after the download has finished. 805 * 806 * @param key Identifies the operation to check on 807 */ 808 public DownloadResults checkOnInstall(Object key) { 809 return checkOnInstall(key, true); 810 } 811 812 /** 813 * Gets the current progress of a download operation. 814 * 815 * @param key Identifies the operation to check on 816 * @param remove If true and the download operation has finished, the DownloadResults 817 * will be forgotten and the next call to this function will return null. 818 * Otherwise, the DownloadResults will be retained until this function is 819 * called with the <tt>remove</tt> parameter set to true. This parameter is 820 * only used when the download operation has finished 821 * (DownloadResults.isFinished() returns true). 822 */ 823 public DownloadResults checkOnInstall(Object key, boolean remove) { 824 DownloadResults results = asyncKeys.get(key); 825 if (results != null) { 826 results = results.duplicate(); 827 if (results.isFinished() && remove) { 828 asyncKeys.remove(key); 829 } 830 } 831 return results; 832 } 833 834 /** 835 * Installs from a pre-downloaded CAR file 836 * 837 * @param carFile care file to install 838 * @param defaultRepository Default repo to look for plugins in 839 * @param restrictToDefaultRepository Whether to follow hints to other plugin repos. 840 * @param username repo username 841 * @param password repo password 842 * @param poller monitor for reporting progress 843 */ 844 public void install(File carFile, String defaultRepository, boolean restrictToDefaultRepository, String username, String password, DownloadPoller poller) { 845 try { 846 // 1. Extract the configuration metadata 847 PluginType data = GeronimoSourceRepository.extractPluginMetadata(carFile); 848 if (data == null) { 849 log.error("Invalid Configuration Archive " + carFile.getAbsolutePath() + " no plugin metadata found"); 850 throw new IllegalArgumentException( 851 "Invalid Configuration Archive " + carFile.getAbsolutePath() + " no plugin metadata found"); 852 } 853 854 // 2. Validate that we can install this 855 validatePlugin(data); 856 verifyPrerequisites(data); 857 858 PluginArtifactType instance = data.getPluginArtifact().get(0); 859 // 3. Install the CAR into the repository (it shouldn't be re-downloaded) 860 if (instance.getModuleId() != null) { 861 Artifact pluginArtifact = toArtifact(instance.getModuleId()); 862 ResultsFileWriteMonitor monitor = new ResultsFileWriteMonitor(poller); 863 writeableRepo.copyToRepository(carFile, pluginArtifact, monitor); 864 installConfigXMLData(pluginArtifact, instance, servers, true); 865 if (instance.getCopyFile() != null) { 866 extractPluginFiles(pluginArtifact, data, monitor); 867 } 868 } 869 870 // 4. Use the standard logic to remove obsoletes, install dependencies, etc. 871 PluginListType pluginList = new PluginListType(); 872 pluginList.getPlugin().add(data); 873 pluginList.getDefaultRepository().addAll(instance.getSourceRepository()); 874 875 SourceRepository defaultSourceRepository = getDefaultSourceRepository(defaultRepository, restrictToDefaultRepository); 876 877 install(pluginList, defaultSourceRepository, restrictToDefaultRepository, username, password, poller, false); 878 } catch (Exception e) { 879 poller.setFailure(e); 880 } finally { 881 poller.setFinished(); 882 } 883 } 884 885 /** 886 * Ensures that a plugin is installable. 887 * 888 * @param plugin plugin to check 889 * @throws org.apache.geronimo.kernel.repository.MissingDependencyException 890 * if plugin requires a dependency that is not present 891 */ 892 public void validatePlugin(PluginType plugin) throws MissingDependencyException { 893 if (plugin.getPluginArtifact().size() != 1) { 894 throw new MissingDependencyException("A plugin configuration must include one plugin artifact, not " + plugin.getPluginArtifact().size(), null, (Stack<Artifact>) null); 895 } 896 PluginArtifactType metadata = plugin.getPluginArtifact().get(0); 897 // 1. Check that it's not already installed 898 if (metadata.getModuleId() != null) { // that is, it's a real configuration not a plugin list 899 Artifact artifact = toArtifact(metadata.getModuleId()); 900 if (configManager.isInstalled(artifact)) { 901 boolean upgrade = false; 902 for (ArtifactType obsolete : metadata.getObsoletes()) { 903 Artifact test = toArtifact(obsolete); 904 if (test.matches(artifact)) { 905 upgrade = true; 906 break; 907 } 908 } 909 if (!upgrade) { 910 log.debug("Configuration " + artifact + " is already installed."); 911 throw new MissingDependencyException( 912 "Configuration " + artifact + " is already installed.", toArtifact(metadata.getModuleId()), (Stack<Artifact>) null); 913 } 914 } 915 } 916 917 // 2. Check that we meet the Geronimo, JVM versions 918 if (metadata.getGeronimoVersion().size() > 0 && !checkGeronimoVersions(metadata.getGeronimoVersion())) { 919 log.debug("Plugin " + toArtifact(metadata.getModuleId()) + " is not installable on Geronimo " + serverInfo.getVersion()); 920 throw new MissingDependencyException( 921 "Plugin is not installable on Geronimo " + serverInfo.getVersion(), toArtifact(metadata.getModuleId()), (Stack<Artifact>) null); 922 } 923 if (metadata.getJvmVersion().size() > 0 && !checkJVMVersions(metadata.getJvmVersion())) { 924 log.debug("Plugin " + toArtifact(metadata.getModuleId()) + " is not installable on JVM " + System.getProperty("java.version")); 925 throw new MissingDependencyException( 926 "Plugin is not installable on JVM " + System.getProperty("java.version"), toArtifact(metadata.getModuleId()), (Stack<Artifact>) null); 927 } 928 } 929 930 931 /** 932 * Ensures that a plugin's prerequisites are installed 933 * 934 * @param plugin plugin artifact to check 935 * @return array of missing depedencies 936 */ 937 public Dependency[] checkPrerequisites(PluginType plugin) { 938 List<Dependency> missingPrereqs = getMissingPrerequisites(plugin); 939 return missingPrereqs.toArray(new Dependency[missingPrereqs.size()]); 940 } 941 942 private List<Dependency> getMissingPrerequisites(PluginType plugin) { 943 if (plugin.getPluginArtifact().size() != 1) { 944 throw new IllegalArgumentException("A plugin configuration must include one plugin artifact, not " + plugin.getPluginArtifact().size()); 945 } 946 947 PluginArtifactType metadata = plugin.getPluginArtifact().get(0); 948 List<PrerequisiteType> prereqs = metadata.getPrerequisite(); 949 950 ArrayList<Dependency> missingPrereqs = new ArrayList<Dependency>(); 951 for (PrerequisiteType prereq : prereqs) { 952 Artifact artifact = toArtifact(prereq.getId()); 953 try { 954 if (getServerInstance("default", servers).getArtifactResolver().queryArtifacts(artifact).length == 0) { 955 missingPrereqs.add(new Dependency(artifact, ImportType.ALL)); 956 } 957 } catch (NoServerInstanceException e) { 958 throw new RuntimeException("Invalid setup, no default server instance registered"); 959 } 960 } 961 return missingPrereqs; 962 } 963 964 private void verifyPrerequisites(PluginType plugin) throws MissingDependencyException { 965 List<Dependency> missingPrereqs = getMissingPrerequisites(plugin); 966 if (!missingPrereqs.isEmpty()) { 967 PluginArtifactType metadata = plugin.getPluginArtifact().get(0); 968 Artifact moduleId = toArtifact(metadata.getModuleId()); 969 StringBuffer buf = new StringBuffer(); 970 buf.append(moduleId.toString()).append(" requires "); 971 Iterator<Dependency> iter = missingPrereqs.iterator(); 972 while (iter.hasNext()) { 973 buf.append(iter.next().getArtifact().toString()); 974 if (iter.hasNext()) { 975 buf.append(", "); 976 } 977 } 978 buf.append(" to be installed"); 979 throw new MissingDependencyException(buf.toString(), null, (Artifact) null); 980 } 981 } 982 983 public Artifact installLibrary(File libFile, String groupId) throws IOException { 984 Matcher matcher = MAVEN_1_PATTERN_PART.matcher(""); 985 matcher.reset(libFile.getName()); 986 if (matcher.matches()) { 987 String artifactId = matcher.group(1); 988 String version = matcher.group(2); 989 String type = matcher.group(3); 990 Artifact artifact = new Artifact(groupId != null ? groupId : Artifact.DEFAULT_GROUP_ID, artifactId, version, type); 991 writeableRepo.copyToRepository(libFile, artifact, null); 992 return artifact; 993 } else { 994 throw new IllegalArgumentException("Filename " + libFile.getName() + " is not in the form <artifact>-<version>.<type>, for e.g. mylib-1.0.jar."); 995 } 996 } 997 998 /** 999 * Download (if necessary) and install something, which may be a Configuration or may 1000 * be just a JAR. For each artifact processed, all its dependencies will be 1001 * processed as well. 1002 * 1003 * @param configID Identifies the artifact to install 1004 * @param metadata name to plugin map 1005 * @param repos The URLs to contact the repositories (in order of preference) 1006 * @param username The username used for repositories secured with HTTP Basic authentication 1007 * @param password The password used for repositories secured with HTTP Basic authentication 1008 * @param monitor The ongoing results of the download operations, with some monitoring logic 1009 * @param soFar The set of dependencies already downloaded. 1010 * @param parentStack chain of modules that led to this dependency 1011 * @param dependency Is this a dependency or the original artifact? 1012 * @param servers server layouts to install config info into 1013 * @param loadOverride If false prevents setting load="true" in server instances (recursively through dependencies) 1014 * @throws FailedLoginException When a repository requires authentication and either no username 1015 * and password are supplied or the username and password supplied 1016 * are not accepted 1017 * @throws MissingDependencyException When a dependency cannot be located in any of the listed repositories 1018 * @throws NoServerInstanceException when no server descriptor is found for a specified configuration bit 1019 * @throws java.io.IOException when a IO problem occurs 1020 */ 1021 private void downloadArtifact(Artifact configID, Map<Artifact, PluginType> metadata, List<SourceRepository> repos, String username, String password, ResultsFileWriteMonitor monitor, Set<Artifact> soFar, Stack<Artifact> parentStack, boolean dependency, Map<String, ServerInstance> servers, boolean loadOverride) throws IOException, FailedLoginException, MissingDependencyException, NoServerInstanceException { 1022 if (soFar.contains(configID)) { 1023 return; // Avoid endless work due to circular dependencies 1024 } else { 1025 soFar.add(configID); 1026 } 1027 // Download and install the main artifact 1028 boolean pluginWasInstalled = false; 1029 Artifact[] matches = configManager.getArtifactResolver().queryArtifacts(configID); 1030 PluginArtifactType instance = null; 1031 if (matches.length == 0) { 1032 // not present, needs to be downloaded 1033 monitor.getResults().setCurrentMessage("Downloading " + configID); 1034 monitor.getResults().setCurrentFilePercent(-1); 1035 OpenResult result = null; 1036 for (SourceRepository repository : repos) { 1037 result = repository.open(configID, monitor); 1038 if (result != null) { 1039 break; 1040 } 1041 } 1042 if (result == null) { 1043 throw new IllegalArgumentException("Could not find " + configID + " in any repo"); 1044 } 1045 // Check if the result is already in server's repository 1046 if (configManager.getArtifactResolver().queryArtifacts(result.getArtifact()).length > 0) { 1047 String msg = "Not downloading " + configID + ". Query for " + configID + " resulted in " + result.getArtifact() 1048 + " which is already available in server's repository."; 1049 monitor.getResults().setCurrentMessage(msg); 1050 log.info(msg); 1051 result.close(); 1052 return; 1053 } 1054 try { 1055 File tempFile = result.getFile(); 1056 if (tempFile == null) { 1057 log.error("Null filehandle was returned for " + configID); 1058 throw new IllegalArgumentException("Null filehandle was returned for " + configID); 1059 } 1060 PluginType pluginData = metadata.get(configID); 1061 // Only bother with the hash if we got it from a source other than the download file itself 1062 HashType hash = pluginData == null ? null : pluginData.getPluginArtifact().get(0).getHash(); 1063 if (hash != null) { 1064 String actual = ConfigurationStoreUtil.getActualChecksum(tempFile, hash.getType()); 1065 if (!actual.equals(hash.getValue())) { 1066 log.error( 1067 "File download incorrect (expected " + hash.getType() + " hash " + hash.getValue() + " but got " + actual + ")"); 1068 throw new IOException( 1069 "File download incorrect (expected " + hash.getType() + " hash " + hash.getValue() + " but got " + actual + ")"); 1070 } 1071 } 1072 // See if the download file has plugin metadata and use it in preference to what is in the catalog. 1073 try { 1074 PluginType realPluginData = GeronimoSourceRepository.extractPluginMetadata(tempFile); 1075 if (realPluginData != null) { 1076 pluginData = realPluginData; 1077 } 1078 } catch (Exception e) { 1079 log.error("Unable to read plugin metadata: " + e.getMessage()); 1080 throw (IOException) new IOException( 1081 "Unable to read plugin metadata: " + e.getMessage()).initCause(e); 1082 } 1083 if (pluginData != null) { // it's a plugin, not a plain JAR 1084 validatePlugin(pluginData); 1085 instance = pluginData.getPluginArtifact().get(0); 1086 } 1087 monitor.getResults().setCurrentMessage("Copying " + result.getArtifact() + " to the repository"); 1088 result.install(writeableRepo, monitor); 1089 if (pluginData != null) { 1090 installConfigXMLData(result.getArtifact(), instance, servers, loadOverride); 1091 } else { 1092 log.debug("No config XML data to install."); 1093 } 1094 if (dependency) { 1095 monitor.getResults().addDependencyInstalled(configID); 1096 configID = result.getArtifact(); 1097 } else { 1098 configID = result.getArtifact(); 1099 monitor.getResults().addInstalledConfigID(configID); 1100 } 1101 pluginWasInstalled = true; 1102 if (pluginData != null) 1103 log.info("Installed plugin with moduleId=" + pluginData.getPluginArtifact().get(0).getModuleId() + " and name=" + pluginData.getName()); 1104 else 1105 log.info("Installed artifact=" + configID); 1106 } catch (InvalidGBeanException e) { 1107 log.error("Invalid gbean configuration ", e); 1108 throw new IllegalStateException( 1109 "Invalid GBean configuration: " + e.getMessage(), e); 1110 1111 } finally { 1112 //todo probably not needede 1113 result.close(); 1114 } 1115 } else { 1116 if (dependency) { 1117 monitor.getResults().addDependencyPresent(configID); 1118 } else { 1119 monitor.getResults().addInstalledConfigID(configID); 1120 } 1121 } 1122 // Download and install the dependencies 1123 try { 1124 if (!configID.isResolved()) { 1125 // See if something's running 1126 for (int i = matches.length - 1; i >= 0; i--) { 1127 Artifact match = matches[i]; 1128 if (configStore.containsConfiguration(match) && configManager.isRunning(match)) { 1129 log.debug("Found required configuration=" + match + " and it is running."); 1130 return; // its dependencies must be OK 1131 } else { 1132 log.debug("Either required configuration=" + match + " is not installed or it is not running."); 1133 } 1134 } 1135 // Go with something that's installed 1136 configID = matches[matches.length - 1]; 1137 } 1138 ConfigurationData data = null; 1139 if (configStore.containsConfiguration(configID)) { 1140 if (configManager.isRunning(configID)) { 1141 return; // its dependencies must be OK 1142 } 1143 log.debug("Loading configuration=" + configID); 1144 data = configStore.loadConfiguration(configID); 1145 } 1146 // Download the dependencies 1147 parentStack.push(configID); 1148 if (instance == null) { 1149 //no plugin metadata, guess with something else 1150 Dependency[] dependencies = data == null ? getDependencies(writeableRepo, configID) : getDependencies(data); 1151 for (Dependency dep : dependencies) { 1152 Artifact artifact = dep.getArtifact(); 1153 log.debug("Attempting to download dependency=" + artifact + " for configuration=" + configID); 1154 downloadArtifact(artifact, metadata, repos, username, password, monitor, soFar, parentStack, true, servers, loadOverride); 1155 } 1156 } else { 1157 //rely on plugin metadata if present. 1158 List<DependencyType> deps = instance.getDependency(); 1159 for (DependencyType dep : deps) { 1160 Artifact artifact = toArtifact(dep); 1161 log.debug("Attempting to download dependency=" + artifact + " for configuration=" + configID); 1162 downloadArtifact(artifact, metadata, repos, username, password, monitor, soFar, parentStack, true, servers, loadOverride & dep.isStart()); 1163 } 1164 } 1165 parentStack.pop(); 1166 } catch (NoSuchConfigException e) { 1167 log.error("Installed configuration into repository but ConfigStore does not see it: " + e.getMessage()); 1168 throw new IllegalStateException( 1169 "Installed configuration into repository but ConfigStore does not see it: " + e.getMessage(), e); 1170 } catch (InvalidConfigException e) { 1171 log.error("Installed configuration into repository but ConfigStore cannot load it: " + e.getMessage()); 1172 throw new IllegalStateException( 1173 "Installed configuration into repository but ConfigStore cannot load it: " + e.getMessage(), e); 1174 } 1175 // Copy any files out of the artifact 1176 PluginType currentPlugin = getPluginMetadata(configID); 1177 if (pluginWasInstalled && currentPlugin != null) { 1178 extractPluginFiles(configID, currentPlugin, monitor); 1179 } 1180 } 1181 1182 private void extractPluginFiles(Artifact configID, PluginType currentPlugin, ResultsFileWriteMonitor monitor) throws IOException { 1183 PluginArtifactType instance = currentPlugin.getPluginArtifact().get(0); 1184 for (CopyFileType data : instance.getCopyFile()) { 1185 monitor.getResults().setCurrentFilePercent(-1); 1186 monitor.getResults().setCurrentFile(data.getValue()); 1187 monitor.getResults().setCurrentMessage("Copying " + data.getValue() + " from plugin to Geronimo installation"); 1188 copyFile(data, configID); 1189 } 1190 } 1191 1192 void copyFile(CopyFileType data, Artifact configID) throws IOException { 1193 Set<URL> set; 1194 String sourceFile = data.getValue().trim(); 1195 try { 1196 set = configStore.resolve(configID, null, sourceFile); 1197 } catch (NoSuchConfigException e) { 1198 log.error("Unable to identify module " + configID + " to copy files from"); 1199 throw new IllegalStateException("Unable to identify module " + configID + " to copy files from", e); 1200 } 1201 if (set.size() == 0) { 1202 log.error("Installed configuration into repository but cannot locate file to copy " + sourceFile); 1203 return; 1204 } 1205 if (set.iterator().next().getPath().endsWith("/")) { 1206 //directory, get all contents 1207 String pattern = sourceFile; 1208 if (pattern.length() == 0 || pattern.endsWith("/")) { 1209 pattern = pattern + "**"; 1210 } else { 1211 pattern = pattern + "/**"; 1212 } 1213 try { 1214 set = new TreeSet<URL>(new Comparator<URL>() { 1215 1216 public int compare(URL o1, URL o2) { 1217 return o1.getPath().compareTo(o2.getPath()); 1218 } 1219 }); 1220 set.addAll(configStore.resolve(configID, null, pattern)); 1221 } catch (NoSuchConfigException e) { 1222 log.error("Unable to list directory " + pattern + " to copy files from"); 1223 throw new IllegalStateException("Unable to list directory " + pattern + " to copy files from", e); 1224 } 1225 } 1226 boolean relativeToServer = "server".equals(data.getRelativeTo()); 1227 String destDir = data.getDestDir(); 1228 File targetDir = relativeToServer ? serverInfo.resolveServer(destDir) : serverInfo.resolve(destDir); 1229 1230 1231 createDirectory(targetDir); 1232 URI targetURI = targetDir.toURI(); 1233 if (!targetDir.isDirectory()) { 1234 log.error( 1235 "Plugin install cannot write file " + data.getValue() + " to " + destDir + " because " + targetDir.getAbsolutePath() + " is not a directory"); 1236 return; 1237 } 1238 if (!targetDir.canWrite()) { 1239 log.error( 1240 "Plugin install cannot write file " + data.getValue() + " to " + destDir + " because " + targetDir.getAbsolutePath() + " is not writable"); 1241 return; 1242 } 1243 int start = -1; 1244 for (URL url : set) { 1245 String path = url.getPath(); 1246 if (start == -1) { 1247 if (sourceFile.length() == 0 || sourceFile.endsWith("/")) { 1248 if ("jar".equals(url.getProtocol())) { 1249 start = path.lastIndexOf("!/") + 2 + sourceFile.length(); 1250 } else { 1251 start = path.length(); 1252 //this entry needs nothing done 1253 continue; 1254 } 1255 } else { 1256 String remove = sourceFile; 1257 int pos = sourceFile.lastIndexOf('/'); 1258 if (pos > -1) { 1259 remove = sourceFile.substring(pos + 1, sourceFile.length()); 1260 } 1261 start = path.lastIndexOf(remove); 1262 } 1263 } 1264 path = path.substring(start); 1265 File target = new File(targetURI.resolve(path)); 1266 if (!target.exists()) { 1267 if (path.endsWith("/")) { 1268 if (!target.mkdirs()) { 1269 log.error("Plugin install cannot create directory " + target.getAbsolutePath()); 1270 } 1271 continue; 1272 } 1273 if (!target.createNewFile()) { 1274 log.error("Plugin install cannot create new file " + target.getAbsolutePath()); 1275 continue; 1276 } 1277 } 1278 if (target.isDirectory()) { 1279 continue; 1280 } 1281 if (!target.canWrite()) { 1282 log.error("Plugin install cannot write to file " + target.getAbsolutePath()); 1283 continue; 1284 } 1285 copyFile(url.openStream(), new FileOutputStream(target)); 1286 } 1287 } 1288 1289 private static void createDirectory(File dir) throws IOException { 1290 if (dir != null && !dir.exists()) { 1291 boolean success = dir.mkdirs(); 1292 if (!success) { 1293 throw new IOException("Cannot create directory " + dir.getAbsolutePath()); 1294 } 1295 } 1296 } 1297 1298 private void copyFile(InputStream in, FileOutputStream out) throws IOException { 1299 byte[] buf = new byte[4096]; 1300 int count; 1301 while ((count = in.read(buf)) > -1) { 1302 out.write(buf, 0, count); 1303 } 1304 in.close(); 1305 out.flush(); 1306 out.close(); 1307 } 1308 1309 /** 1310 * Used to get dependencies for a JAR 1311 * 1312 * @param repo repository containing jar 1313 * @param artifact artifact to find dependencies of 1314 * @return dependencies of artifact in repository 1315 */ 1316 private static Dependency[] getDependencies(Repository repo, Artifact artifact) { 1317 Set<Artifact> set = repo.getDependencies(artifact); 1318 Dependency[] results = new Dependency[set.size()]; 1319 int index = 0; 1320 for (Artifact dep : set) { 1321 results[index] = new Dependency(dep, ImportType.CLASSES); 1322 ++index; 1323 } 1324 return results; 1325 } 1326 1327 /** 1328 * Used to get dependencies for a Configuration 1329 * 1330 * @param data configuration data 1331 * @return dependencies of configuration 1332 */ 1333 private static Dependency[] getDependencies(ConfigurationData data) { 1334 List<Dependency> dependencies = new ArrayList<Dependency>(data.getEnvironment().getDependencies()); 1335 Collection<ConfigurationData> children = data.getChildConfigurations().values(); 1336 for (ConfigurationData child : children) { 1337 dependencies.addAll(child.getEnvironment().getDependencies()); 1338 } 1339 return dependencies.toArray(new Dependency[dependencies.size()]); 1340 } 1341 1342 /** 1343 * Searches for an artifact in the listed repositories, where the artifact 1344 * may have wildcards in the ID. 1345 */ 1346 1347 /** 1348 * Puts the name and ID of a plugin into the argument map of plugins, 1349 * by reading the values out of the provided plugin descriptor file. 1350 * 1351 * @param xml The geronimo-plugin.xml for this plugin 1352 * @param plugins The result map to populate 1353 */ 1354 private void readNameAndID(File xml, Map<String, Artifact> plugins) { 1355 try { 1356 SAXParserFactory factory = XmlUtil.newSAXParserFactory(); 1357 SAXParser parser = factory.newSAXParser(); 1358 PluginNameIDHandler handler = new PluginNameIDHandler(); 1359 parser.parse(xml, handler); 1360 if (handler.isComplete()) { 1361 plugins.put(handler.getName(), Artifact.create(handler.getID())); 1362 } 1363 } catch (Exception e) { 1364 log.warn("Invalid XML at " + xml.getAbsolutePath(), e); 1365 } 1366 } 1367 1368 /** 1369 * Puts the name and ID of a plugin into the argument map of plugins, 1370 * by reading the values out of the provided plugin descriptor stream. 1371 * 1372 * @param xml The geronimo-plugin.xml for this plugin 1373 * @param plugins The result map to populate 1374 */ 1375 private void readNameAndID(InputStream xml, Map plugins) { 1376 try { 1377 SAXParserFactory factory = XmlUtil.newSAXParserFactory(); 1378 SAXParser parser = factory.newSAXParser(); 1379 PluginNameIDHandler handler = new PluginNameIDHandler(); 1380 parser.parse(xml, handler); 1381 if (handler.isComplete()) { 1382 plugins.put(handler.getName(), Artifact.create(handler.getID())); 1383 } 1384 } catch (Exception e) { 1385 log.warn("Invalid XML", e); 1386 } 1387 } 1388 1389 /** 1390 * TODO figure out where this can be called -- perhaps when installing a plugin? 1391 * Generates a default plugin metadata based on the data for this module 1392 * in the server. 1393 */ 1394 private PluginType createDefaultMetadata(Artifact moduleId) throws InvalidConfigException, IOException, NoSuchConfigException { 1395 if (configManager != null) { 1396 if (!configManager.isConfiguration(moduleId)) { 1397 return null; 1398 } 1399 } else { 1400 if (!configStore.containsConfiguration(moduleId)) { 1401 return null; 1402 } 1403 } 1404 ConfigurationData data = configStore.loadConfiguration(moduleId); 1405 1406 PluginType meta = new PluginType(); 1407 PluginArtifactType instance = new PluginArtifactType(); 1408 meta.getPluginArtifact().add(instance); 1409 meta.setName(toArtifactType(moduleId).getArtifactId()); 1410 instance.setModuleId(toArtifactType(moduleId)); 1411 meta.setCategory("Unknown"); 1412 instance.getGeronimoVersion().add(serverInfo.getVersion()); 1413 instance.getObsoletes().add(toArtifactType(new Artifact(moduleId.getGroupId(), 1414 moduleId.getArtifactId(), 1415 (Version) null, 1416 moduleId.getType()))); 1417 List<DependencyType> deps = instance.getDependency(); 1418 addGeronimoDependencies(data, deps, true); 1419 return meta; 1420 } 1421 1422 /** 1423 * Check whether the specified JVM versions match the current runtime 1424 * environment. 1425 * 1426 * @param jvmVersions allowed jvm versions 1427 * @return true if the specified versions match the current 1428 * execution environment as defined by plugins-1.2.xsd 1429 */ 1430 private boolean checkJVMVersions(List<String> jvmVersions) { 1431 if (jvmVersions.size() == 0) return true; 1432 String version = System.getProperty("java.version"); 1433 boolean match = false; 1434 for (String jvmVersion : jvmVersions) { 1435 if (jvmVersion == null || jvmVersion.equals("")) { 1436 log.error("jvm-version should not be empty."); 1437 throw new IllegalStateException("jvm-version should not be empty."); 1438 } 1439 if (version.startsWith(jvmVersion)) { 1440 match = true; 1441 break; 1442 } 1443 } 1444 return match; 1445 } 1446 1447 /** 1448 * Check whether the specified Geronimo versions match the current runtime 1449 * environment. 1450 * 1451 * @param gerVersions geronimo versions allowed by the plugin 1452 * @return true if the specified versions match the current 1453 * execution environment as defined by plugins-1.2.xsd 1454 * @throws IllegalStateException if match does not work. 1455 */ 1456 private boolean checkGeronimoVersions(List<String> gerVersions) throws IllegalStateException { 1457 if ((gerVersions == null) || (gerVersions.size() == 0)) { 1458 return true; 1459 } 1460 1461 boolean match = false; 1462 for (String gerVersion : gerVersions) { 1463 match = checkGeronimoVersion(gerVersion); 1464 if (match) { 1465 break; 1466 } 1467 } 1468 return match; 1469 } 1470 1471 /** 1472 * Check whether the specified Geronimo version matches the current runtime 1473 * environment. 1474 * 1475 * @param gerVersion geronimo version to check against this server 1476 * @return true if the specified version matches the current 1477 * execution environment as defined by plugins-1.2.xsd 1478 * @throws IllegalStateException if input is malformed 1479 */ 1480 private boolean checkGeronimoVersion(String gerVersion) throws IllegalStateException { 1481 String version = serverInfo.getVersion(); 1482 1483 if ((gerVersion == null) || gerVersion.equals("")) { 1484 log.error("geronimo-version cannot be empty."); 1485 throw new IllegalStateException("geronimo-version cannot be empty."); 1486 } else { 1487 return gerVersion.equals(version); 1488 } 1489 } 1490 1491 public static void addGeronimoDependencies(ConfigurationData data, List<DependencyType> deps, boolean includeVersion) { 1492 processDependencyList(data.getEnvironment().getDependencies(), deps, includeVersion); 1493 Map<String, ConfigurationData> children = data.getChildConfigurations(); 1494 for (ConfigurationData child : children.values()) { 1495 processDependencyList(child.getEnvironment().getDependencies(), deps, includeVersion); 1496 } 1497 } 1498 1499 /** 1500 * Generates dependencies and an optional prerequisite based on a list of 1501 * dependencies for a Gernonimo module. 1502 * 1503 * @param real A list with elements of type Dependency 1504 * @param deps A list with elements of type String (holding a module ID / Artifact name) 1505 * @param includeVersion whether to include a version in the plugin xml dependency 1506 */ 1507 private static void processDependencyList(List<Dependency> real, List<DependencyType> deps, boolean includeVersion) { 1508 for (Dependency dep : real) { 1509 DependencyType dependency = toDependencyType(dep, includeVersion); 1510 if (!deps.contains(dependency)) { 1511 deps.add(dependency); 1512 } 1513 } 1514 } 1515 1516 public static DependencyType toDependencyType(Dependency dep, boolean includeVersion) { 1517 Artifact id = dep.getArtifact(); 1518 DependencyType dependency = new DependencyType(); 1519 dependency.setGroupId(id.getGroupId()); 1520 dependency.setArtifactId(id.getArtifactId()); 1521 if (includeVersion) { 1522 dependency.setVersion(id.getVersion() == null ? null : id.getVersion().toString()); 1523 } 1524 dependency.setType(id.getType()); 1525 return dependency; 1526 } 1527 1528 public static Artifact toArtifact(ArtifactType moduleId) { 1529 String groupId = moduleId.getGroupId(); 1530 String artifactId = moduleId.getArtifactId(); 1531 String version = moduleId.getVersion(); 1532 String type = moduleId.getType(); 1533 return new Artifact(groupId, artifactId, version, type); 1534 } 1535 1536 public static ArtifactType toArtifactType(Artifact id) { 1537 ArtifactType artifact = new ArtifactType(); 1538 artifact.setGroupId(id.getGroupId()); 1539 artifact.setArtifactId(id.getArtifactId()); 1540 artifact.setVersion(id.getVersion() == null ? null : id.getVersion().toString()); 1541 artifact.setType(id.getType()); 1542 return artifact; 1543 } 1544 1545 public static PluginType copy(PluginType metadata, PluginArtifactType instance) { 1546 PluginType copy = new PluginType(); 1547 copy.setAuthor(metadata.getAuthor()); 1548 copy.setCategory(metadata.getCategory()); 1549 copy.setDescription(metadata.getDescription()); 1550 copy.setName(metadata.getName()); 1551 copy.setUrl(metadata.getUrl()); 1552 copy.getLicense().addAll(metadata.getLicense()); 1553 if (instance != null) { 1554 copy.getPluginArtifact().add(instance); 1555 } 1556 return copy; 1557 } 1558 1559 public static PluginType toKey(PluginType metadata) { 1560 PluginType copy = new PluginKey(); 1561 copy.setAuthor(metadata.getAuthor()); 1562 copy.setCategory(metadata.getCategory()); 1563 copy.setName(metadata.getName()); 1564 copy.setUrl(metadata.getUrl()); 1565 copy.getLicense().addAll(metadata.getLicense()); 1566 return copy; 1567 } 1568 1569 private static class PluginKey extends PluginType { 1570 private static final long serialVersionUID = -3864898789387102435L; 1571 1572 public boolean equals(Object o) { 1573 if (this == o) return true; 1574 if (o == null || getClass() != o.getClass()) return false; 1575 1576 PluginKey that = (PluginKey) o; 1577 1578 if (author != null ? !author.equals(that.author) : that.author != null) return false; 1579 if (category != null ? !category.equals(that.category) : that.category != null) return false; 1580 if (name != null ? !name.equals(that.name) : that.name != null) return false; 1581 if (url != null ? !url.equals(that.url) : that.url != null) return false; 1582 if ((license == null) != (that.license == null)) return false; 1583 if (license != null) { 1584 if (license.size() != that.license.size()) return false; 1585 int i = 0; 1586 for (LicenseType licenseType : license) { 1587 LicenseType otherLicense = that.license.get(i++); 1588 if (licenseType.isOsiApproved() != otherLicense.isOsiApproved()) return false; 1589 if (licenseType.getValue() != null ? !licenseType.getValue().equals(otherLicense.getValue()) : otherLicense.getValue() != null) return false; 1590 } 1591 } 1592 1593 return true; 1594 } 1595 1596 public int hashCode() { 1597 int result; 1598 result = (name != null ? name.hashCode() : 0); 1599 result = 31 * result + (category != null ? category.hashCode() : 0); 1600 result = 31 * result + (url != null ? url.hashCode() : 0); 1601 result = 31 * result + (author != null ? author.hashCode() : 0); 1602 return result; 1603 } 1604 } 1605 1606 public PluginListType createPluginListForRepositories(String repo) throws NoSuchStoreException { 1607 PluginListType pluginList = localSourceRepository.getPluginList(); 1608 if (repo != null) { 1609 pluginList.getDefaultRepository().add(repo); 1610 } 1611 return pluginList; 1612 } 1613 1614 1615 /** 1616 * If a plugin includes config.xml content, copy it into the attribute 1617 * store. 1618 * 1619 * @param configID artifact we are installing 1620 * @param pluginData metadata for plugin 1621 * @param servers server metadata that might be modified 1622 * @param loadOverride overrides the load setting from plugin metadata in a config.xml module. 1623 * @throws java.io.IOException if IO problem occurs 1624 * @throws org.apache.geronimo.kernel.InvalidGBeanException 1625 * if an invalid gbean configuration is encountered 1626 * @throws NoServerInstanceException if the plugin expects a server metadata that is not present 1627 */ 1628 private void installConfigXMLData(Artifact configID, PluginArtifactType pluginData, Map<String, ServerInstance> servers, boolean loadOverride) throws InvalidGBeanException, IOException, NoServerInstanceException { 1629 if (configManager.isConfiguration(configID)) { 1630 if (pluginData != null && !pluginData.getConfigXmlContent().isEmpty()) { 1631 for (ConfigXmlContentType configXmlContent : pluginData.getConfigXmlContent()) { 1632 String serverName = configXmlContent.getServer(); 1633 ServerInstance serverInstance = getServerInstance(serverName, servers); 1634 serverInstance.getAttributeStore().setModuleGBeans(configID, configXmlContent.getGbean(), loadOverride && configXmlContent.isLoad(), configXmlContent.getCondition()); 1635 } 1636 } else { 1637 getServerInstance("default", servers).getAttributeStore().setModuleGBeans(configID, null, loadOverride, null); 1638 } 1639 } 1640 if (pluginData == null) { 1641 return; 1642 } 1643 if (!pluginData.getConfigSubstitution().isEmpty()) { 1644 Map<String, Properties> propertiesMap = toPropertiesMap(pluginData.getConfigSubstitution()); 1645 for (Map.Entry<String, Properties> entry : propertiesMap.entrySet()) { 1646 String serverName = entry.getKey(); 1647 ServerInstance serverInstance = getServerInstance(serverName, servers); 1648 serverInstance.getAttributeStore().addConfigSubstitutions(entry.getValue()); 1649 } 1650 } 1651 if (!pluginData.getArtifactAlias().isEmpty()) { 1652 Map<String, Properties> propertiesMap = toPropertiesMap(pluginData.getArtifactAlias()); 1653 for (Map.Entry<String, Properties> entry : propertiesMap.entrySet()) { 1654 String serverName = entry.getKey(); 1655 ServerInstance serverInstance = getServerInstance(serverName, servers); 1656 serverInstance.getArtifactResolver().addAliases(entry.getValue()); 1657 } 1658 } 1659 } 1660 1661 private ServerInstance getServerInstance(String serverName, Map<String, ServerInstance> servers) throws NoServerInstanceException { 1662 ServerInstance serverInstance = servers.get(serverName); 1663 if (serverInstance == null) { 1664 throw new NoServerInstanceException("No server instance configuration set up for name " + serverName); 1665 } 1666 return serverInstance; 1667 } 1668 1669 private Map<String, Properties> toPropertiesMap(List<PropertyType> propertyTypes) { 1670 Map<String, Properties> propertiesMap = new HashMap<String, Properties>(); 1671 for (PropertyType propertyType : propertyTypes) { 1672 String serverName = propertyType.getServer(); 1673 Properties properties = propertiesMap.get(serverName); 1674 if (properties == null) { 1675 properties = new Properties(); 1676 propertiesMap.put(serverName, properties); 1677 } 1678 properties.setProperty(propertyType.getKey(), propertyType.getValue()); 1679 } 1680 return propertiesMap; 1681 } 1682 1683 /** 1684 * Gets a token unique to this run of the server, used to track asynchronous 1685 * downloads. 1686 * 1687 * @return unique (for this server) key 1688 */ 1689 private static Object getNextKey() { 1690 int value; 1691 synchronized (PluginInstallerGBean.class) { 1692 value = ++counter; 1693 } 1694 return value; 1695 } 1696 1697 1698 /** 1699 * Helper clas to extract a name and module ID from a plugin metadata file. 1700 */ 1701 private static class PluginNameIDHandler extends DefaultHandler { 1702 private String id = ""; 1703 private String name = ""; 1704 private String element = null; 1705 1706 public void characters(char ch[], int start, int length) throws SAXException { 1707 if (element != null) { 1708 if (element.equals("module-id")) { 1709 id += new String(ch, start, length); 1710 } else if (element.equals("name")) { 1711 name += new String(ch, start, length); 1712 } 1713 } 1714 } 1715 1716 public void endElement(String uri, String localName, String qName) throws SAXException { 1717 element = null; 1718 } 1719 1720 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 1721 if (qName.equals("module-id") || qName.equals("name")) { 1722 element = qName; 1723 } 1724 } 1725 1726 public void endDocument() throws SAXException { 1727 id = id.trim(); 1728 name = name.trim(); 1729 } 1730 1731 public String getID() { 1732 return id; 1733 } 1734 1735 public String getName() { 1736 return name; 1737 } 1738 1739 public boolean isComplete() { 1740 return !id.equals("") && !name.equals(""); 1741 } 1742 } 1743 1744 /** 1745 * Helper class to bridge a FileWriteMonitor to a DownloadPoller. 1746 */ 1747 private static class ResultsFileWriteMonitor implements FileWriteMonitor { 1748 private final DownloadPoller results; 1749 private int totalBytes; 1750 private String file; 1751 1752 public ResultsFileWriteMonitor(DownloadPoller results) { 1753 this.results = results; 1754 } 1755 1756 public void setTotalBytes(int totalBytes) { 1757 this.totalBytes = totalBytes; 1758 } 1759 1760 public int getTotalBytes() { 1761 return totalBytes; 1762 } 1763 1764 public void writeStarted(String fileDescription, int fileSize) { 1765 totalBytes = fileSize; 1766 file = fileDescription; 1767 results.setCurrentFile(fileDescription); 1768 results.setCurrentFilePercent(totalBytes > 0 ? 0 : -1); 1769 results.setCurrentMessage("Downloading " + file); 1770 } 1771 1772 public void writeProgress(int bytes) { 1773 if (totalBytes > 0) { 1774 double percent = (double) bytes / (double) totalBytes; 1775 results.setCurrentFilePercent((int) (percent * 100)); 1776 } else { 1777 results.setCurrentMessage((bytes / 1024) + " kB of " + file); 1778 } 1779 } 1780 1781 public void writeComplete(int bytes) { 1782 results.setCurrentFilePercent(100); 1783 results.setCurrentMessage("Finished downloading " + file + " (" + (bytes / 1024) + " kB)"); 1784 results.addDownloadBytes(bytes); 1785 } 1786 1787 public DownloadPoller getResults() { 1788 return results; 1789 } 1790 } 1791 1792 public static final GBeanInfo GBEAN_INFO; 1793 1794 static { 1795 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(PluginInstallerGBean.class); 1796 infoFactory.addInterface(PluginInstaller.class); 1797 infoFactory.addReference("ConfigManager", ConfigurationManager.class, "ConfigurationManager"); 1798 infoFactory.addReference("Repository", WritableListableRepository.class, "Repository"); 1799 infoFactory.addReference("ConfigStore", ConfigurationStore.class, "ConfigurationStore"); 1800 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean"); 1801 infoFactory.addReference("ThreadPool", ThreadPool.class, "GBean"); 1802 infoFactory.addReference("ServerInstances", ServerInstanceData.class, "ServerInstanceData"); 1803 infoFactory.addReference("ArtifactManager", ArtifactManager.class, "GBean"); 1804 infoFactory.addReference("PersistentConfigurationLists", PersistentConfigurationList.class, "AttributeStore"); 1805 infoFactory.addAttribute("classLoader", ClassLoader.class, false); 1806 infoFactory.setConstructor(new String[]{"ConfigManager", "Repository", "ConfigStore", 1807 "ServerInstances", "ServerInfo", "ThreadPool", "ArtifactManager", "PersistentConfigurationLists", "classLoader"}); 1808 1809 GBEAN_INFO = infoFactory.getBeanInfo(); 1810 } 1811 1812 public static GBeanInfo getGBeanInfo() { 1813 return GBEAN_INFO; 1814 } 1815 }