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 }