KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > extensions > ExtensionBundle


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.sslexplorer.extensions;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Iterator JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.jdom.Attribute;
32 import org.jdom.DataConversionException;
33 import org.jdom.Document;
34 import org.jdom.Element;
35 import org.jdom.JDOMException;
36 import org.jdom.input.SAXBuilder;
37
38 import com.sslexplorer.boot.Util;
39 import com.sslexplorer.boot.VersionInfo;
40 import com.sslexplorer.boot.VersionInfo.Version;
41 import com.sslexplorer.extensions.store.ExtensionStore;
42 import com.sslexplorer.extensions.types.Plugin;
43 import com.sslexplorer.extensions.types.PluginType;
44
45 /**
46  * Extension bundles describe a collection of <i>Extension Descriptors</i>.
47  * <p>
48  * A bundle may come from one of two places, either the <i>3SP Application Store</i>
49  * or from an installed extension bundle retrieved via the <i>Repository</i>.
50  * <p>
51  * If from the 3SP Application Store, the descriptor will not contain all
52  * details, only enough to determine the name, description, versions,
53  * dependencies and a few others pieces of information. Such bundles do not have
54  * a life cycle in the way local extension bundles do (i.e. be <i>Started</i>,
55  * <i>Stopped</i> or <i>Activated</i>).
56  * <p>
57  * Local extension bundles must go through 3 or 4 phases.
58  * <ul>
59  * <li>Phase 1. Loading. The XML extension descriptors are loaded and parsed.
60  * No change the state of SSL-Explorer is made at this stage. Once this is
61  * complete we know the order the extensions should be started</li>
62  * <li>Phase 2. Starting. All extensions now have their
63  * {@link ExtensionDescriptor#start()} method called. This in turn delegates the
64  * start to the <i>Extension Type</i> implementation in use. For example, the
65  * {@link PluginType} would create the plug-in instance and invoke the
66  * {@link Plugin#startPlugin(com.sslexplorer.extensions.types.PluginDefinition, ExtensionDescriptor, Element)}.
67  * If starting fails, an exception is thrown and the extension will be
68  * <i>Stopped</i>. method on it.</li>
69  * <li>Phase 3. Activation. Only the <i>Plug-ins</i> really use this as they
70  * require two phases of initialisation. If activate fails, an exception is
71  * thrown and the extension will be <i>Stopped</i>. </li>
72  * <li>Phase 4. Stopping. May occur either after <i>Starting</i> or
73  * <i>Activation</i>. During this phase the extension should clean up as much
74  * as possible (deregister property definitions, user databases or any other
75  * extension point).
76  * </ul>
77  *
78  *
79  * @author Brett Smith <a HREF="mailto:brett@3sp.com">&lt;brett@3sp.com&gt;</a>
80  */

81 public class ExtensionBundle extends ArrayList JavaDoc<ExtensionDescriptor> implements Comparable JavaDoc {
82
83     final static Log log = LogFactory.getLog(ExtensionBundle.class);
84
85     /**
86      * Extension 'type' code indicating a new version is available from the 3SP
87      * Application Store
88      */

89     public static final int TYPE_UPDATEABLE = 0;
90
91     /**
92      * Extension 'type' code indicating the most up to date version in 3SP
93      * Application Store is already correctly installed
94      */

95     public static final int TYPE_INSTALLED = 1;
96
97     /**
98      * Extension 'type' code indicating the bundle is not currently installed
99      * but available from the 3SP Application Store
100      */

101     public static final int TYPE_INSTALLABLE = 2;
102
103     /**
104      * Extension 'type' code indicating the bundle is not an installable bundle,
105      * merely a pointer to further instructions as to how to create a complete
106      * bundle (this is for bundles that we cannot legally distribute all
107      * components)
108      */

109     public static final int TYPE_CONFIGUREABLE = 3;
110
111     /**
112      * Extension 'type' code indicating the bundle has been removed by the
113      * administrator but cannot yet be deleted from the local file system as it
114      * contains plug-ins that are in use.
115      */

116     public static final int TYPE_PENDING_REMOVAL = 4;
117
118     /**
119      * Extension 'type' code indicating a new bundle has been installed but
120      * cannot be started because it contains plug-ins. The administrator should
121      * restart the entire server to complete the installation.
122      */

123     public static final int TYPE_PENDING_INSTALLATION = 5;
124
125     /**
126      * Extension 'type' code indicating a bundle has been updated but cannot be
127      * restarted because it contains plug-ins. The administrator should restart
128      * the entire server to complete the update.
129      */

130     public static final int TYPE_PENDING_UPDATE = 6;
131
132     /**
133      * Extension 'type' code indicating a bundle state has been changed
134      * (i.e. enable or disable) but cannot be restarted because it contains
135      * plug-ins. The administrator should restart the entire server to complete
136      * the change.
137      */

138     public static final int TYPE_PENDING_STATE_CHANGE = 7;
139
140     /**
141      * Status of extension bundle
142      */

143     public enum ExtensionBundleStatus {
144         /**
145          * The extension is enabled, but stopped
146          */

147         ENABLED(0, "enabled"),
148
149         /**
150          * The extension is disabled
151          */

152         DISABLED(1, "disabled"),
153
154         /**
155          * The extension has been disabled by the system (cannot be enabled
156          */

157         SYSTEM_DISABLED(2, "systemDisabled"),
158
159         /**
160          * The extension is enabled and started
161          */

162         STARTED(3, "started"),
163
164         /**
165          * The extension is enabled and activated
166          */

167         ACTIVATED(4, "activated"),
168
169         /**
170          * The extension is errored
171          */

172         ERROR(6, "error");
173
174         private String JavaDoc name;
175
176         private ExtensionBundleStatus(int state, String JavaDoc name) {
177             this.name = name;
178         }
179
180         /**
181          * Determine if the state is {@link #STARTED} or {@link #ACTIVATED}.
182          *
183          * @return started or activated
184          */

185         public boolean isStartedOrActivated() {
186             return this == ACTIVATED || this == STARTED;
187         }
188
189         /**
190          * Get the status name
191          *
192          * @return name
193          */

194         public String JavaDoc getName() {
195             return name;
196         }
197
198         /**
199          * @return boolean
200          */

201         public boolean isDisabled() {
202             return this == DISABLED || this == SYSTEM_DISABLED;
203         }
204     }
205
206     // Private instance variables
207

208     private File JavaDoc descriptor;
209     private Document doc;
210     private String JavaDoc description;
211     private String JavaDoc license;
212     private String JavaDoc productURL;
213     private String JavaDoc instructionsURL;
214     private VersionInfo.Version version;
215     private int type;
216     private int order;
217     private String JavaDoc id;
218     private String JavaDoc name;
219     private String JavaDoc licenseFilePath;
220     private VersionInfo.Version requiredHostVersion;
221     private ExtensionInstaller installer;
222     private String JavaDoc category;
223     private boolean mandatoryUpdate;
224     private ExtensionBundleStatus status = ExtensionBundleStatus.ENABLED;
225     private Collection JavaDoc<String JavaDoc> dependencyNames;
226     private Throwable JavaDoc error;
227     private boolean hidden;
228     private boolean devExtension;
229     private Element messageElement;
230     private String JavaDoc changes;
231     private VersionInfo.Version updateVersion;
232
233     /**
234      * Constructor for when creating a bundle without having a XML extension
235      * descriptor stream.
236      *
237      * @param version
238      * @param type
239      * @param id
240      * @param name
241      * @param description
242      * @param license
243      * @param productURL
244      * @param instructionsURL
245      * @param requiredHostVersion
246      * @param dependencyNames collection of dependency names or
247      * <code>null</code> for no dependencies
248      * @param category
249      * @param mandatoryUpdate
250      * @param order
251      * @param changes
252      */

253     public ExtensionBundle(Version version, int type, String JavaDoc id, String JavaDoc name, String JavaDoc description, String JavaDoc license,
254                             String JavaDoc productURL, String JavaDoc instructionsURL, VersionInfo.Version requiredHostVersion,
255                             Collection JavaDoc<String JavaDoc> dependencyNames, String JavaDoc category, boolean mandatoryUpdate, int order,
256                             String JavaDoc changes) {
257         this.version = version;
258         this.type = type;
259         this.id = id;
260         this.name = name;
261         this.description = description;
262         this.license = license;
263         this.productURL = productURL;
264         this.order = order;
265         this.instructionsURL = instructionsURL;
266         this.requiredHostVersion = requiredHostVersion;
267         this.dependencyNames = dependencyNames;
268         this.category = category;
269         this.mandatoryUpdate = mandatoryUpdate;
270         this.changes = changes;
271     }
272
273     /**
274      * Constructor for creating a new bundle given a file that contains an XML
275      * extension bundle descriptor.
276      *
277      * @param descriptor descriptor
278      * @param devExtension loaded as dev extension
279      */

280     public ExtensionBundle(File JavaDoc descriptor, boolean devExtension) {
281         this.descriptor = descriptor;
282         this.devExtension = devExtension;
283     }
284     
285     /**
286      * Get if this bundle was loaded as a <i>devExtension</i>.
287      *
288      * @return dev extension
289      */

290     public boolean isDevExtension() {
291         return devExtension;
292     }
293
294     /**
295      * Get if this bundle is hidden
296      *
297      * @return hidden
298      */

299     public boolean isHidden() {
300         return hidden;
301     }
302
303     /**
304      * Get the extension bundle ID.
305      *
306      * @return bundle ID
307      */

308     public String JavaDoc getId() {
309         return id;
310     }
311     
312     /**
313      * Determines if the extension bundle is updateable.
314      * @return boolean
315      */

316     public boolean isUpdateable() {
317         return getType() == ExtensionBundle.TYPE_UPDATEABLE;
318     }
319
320     /**
321      * Get the order. This determines which order the bundle will get loaded
322      * started and activated in (in relative to other extensions). The lower the
323      * number the earlier the extension should be loaded
324      *
325      * @return order
326      */

327     public int getOrder() {
328         return order;
329     }
330
331     /**
332      * Get the english name of this extension bundle.
333      *
334      * @return english name of bundle
335      */

336     public String JavaDoc getName() {
337         return name;
338     }
339
340     /**
341      * Get the extension descriptor file that represents this bundle. If the
342      * bundle is not loaded locally (i.e. it came from the 3SP Application
343      * Store) then this will be <code>null</code>.
344      *
345      * @return extension descriptor file
346      */

347     public File JavaDoc getFile() {
348         return descriptor;
349     }
350
351     /**
352      * Get the category for this bundle. This is used in the extension manager
353      * front end to group available extensions.
354      *
355      * @return category
356      */

357     public String JavaDoc getCategory() {
358         return category;
359     }
360
361     /**
362      * Get a collection of the names of the extensions bundles this bundle
363      * depends on. If any of the dependencies are not satisfied the extension
364      * may not be started. If there are no dependencies <code>null</code> will
365      * be returned.
366      *
367      * @return extension bundle dependency names
368      */

369     public Collection JavaDoc<String JavaDoc> getDependencies() {
370         return dependencyNames;
371     }
372
373     /**
374      * Start all extension bundles. This method will start all extension
375      * descriptors it contains and is the second phase in an extensions life
376      * cycle (after loading).
377      *
378      * @throws ExtensionException if any bundle could not be started
379      */

380     public synchronized void start() throws ExtensionException {
381
382         try {
383             if (log.isInfoEnabled()) {
384                 log.info("Starting extension bundle " + getId());
385             }
386
387             // Check we are allowed to start this bundle
388
if (getStatus() != ExtensionBundleStatus.ENABLED) {
389                 throw new ExtensionException(ExtensionException.INVALID_EXTENSION_BUNDLE_STATUS,
390                                 getId(),
391                                 "Bundle is not in enabled state.");
392             }
393
394             // Check this bundles dependencies are installed and started
395
checkDependenciesStarted(this);
396
397             // Start all extensions in this bundle
398
ExtensionException ee = null;
399             for (Iterator JavaDoc i = iterator(); i.hasNext();) {
400                 ExtensionDescriptor d = (ExtensionDescriptor) i.next();
401                 try {
402                     d.start();
403                     
404                     // Set any bundle messages
405
setBundleMessages(d);
406                     
407                     status = ExtensionBundleStatus.STARTED;
408                 } catch (ExtensionException ex) {
409                     if (ee == null) {
410                         ee = ex;
411                     }
412                 }catch (Throwable JavaDoc t){
413                     if (ee == null) {
414                         ee = new ExtensionException(ExtensionException.INTERNAL_ERROR, t);
415                     }
416                 }
417             }
418             if (ee != null) {
419                 throw ee;
420             }
421         } catch (ExtensionException ee) {
422             log.error("Failed to start extension. ", ee);
423             error = ee;
424             status = ExtensionBundleStatus.ERROR;
425             throw ee;
426         }
427
428         error = null;
429     }
430
431     private void setBundleMessages(ExtensionDescriptor d) throws ExtensionException {
432         if(messageElement != null) {
433             for (Iterator JavaDoc i2 = messageElement.getChildren().iterator(); i2.hasNext();) {
434                 Element el = (Element) i2.next();
435                 if (!el.getName().equals("message")) {
436                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
437                                     "<messages> element may only contain <message> elements.");
438                 }
439                 String JavaDoc key = el.getAttributeValue("key");
440                 if (key == null) {
441                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
442                                     "<message> element must have a key attribute.");
443                 }
444                 String JavaDoc aKey = "application." + d.getId() + "." + key;
445                 if (d.getMessageResources() != null) {
446                     if (!d.getMessageResources().isPresent(key)) {
447                         d.getMessageResources().setMessage(el.getAttributeValue("locale"), aKey, el.getText());
448                     }
449                 }
450             }
451         }
452     }
453
454     void checkDependenciesStarted(ExtensionBundle bundle) throws ExtensionException {
455         if (bundle.getDependencies() != null) {
456             for (String JavaDoc dep : bundle.getDependencies()) {
457                 if (!ExtensionStore.getInstance().isExtensionBundleLoaded(dep)) {
458                     throw new ExtensionException(ExtensionException.DEPENDENCY_NOT_INSTALLED, dep, getId());
459                 }
460                 ExtensionBundle depBundle = ExtensionStore.getInstance().getExtensionBundle(dep);
461                 if (!depBundle.getStatus().isStartedOrActivated()) {
462                     throw new ExtensionException(ExtensionException.DEPENDENCY_NOT_STARTED, dep, getId());
463                 }
464                 checkDependenciesStarted(depBundle);
465             }
466         }
467
468     }
469
470     void checkDependenciesActivated(ExtensionBundle bundle) throws ExtensionException {
471         if (bundle.getDependencies() != null) {
472             for (String JavaDoc dep : bundle.getDependencies()) {
473                 if (!ExtensionStore.getInstance().isExtensionBundleLoaded(dep)) {
474                     throw new ExtensionException(ExtensionException.DEPENDENCY_NOT_INSTALLED, dep, getId());
475                 }
476                 ExtensionBundle depBundle = ExtensionStore.getInstance().getExtensionBundle(dep);
477                 if (depBundle.getStatus() != ExtensionBundleStatus.ACTIVATED) {
478                     throw new ExtensionException(ExtensionException.DEPENDENCY_NOT_STARTED, dep, getId());
479                 }
480                 checkDependenciesActivated(depBundle);
481             }
482         }
483
484     }
485
486     /**
487      * Activate all extension bundles. This method will active all extension
488      * descriptors it contains and is the third phase in an extensions life
489      * cycle (after starting).
490      *
491      * @throws ExtensionException if any bundle could not be activated
492      */

493     public synchronized void activate() throws ExtensionException {
494         try {
495             if (getStatus() != ExtensionBundleStatus.STARTED) {
496                 throw new ExtensionException(ExtensionException.INVALID_EXTENSION_BUNDLE_STATUS,
497                                 getId(),
498                                 "Bundle is not in started so cannot be activated.");
499             }
500
501             // Check this bundles dependencies are installed and started
502
checkDependenciesActivated(this);
503             
504             ExtensionException ee = null;
505             for (Iterator JavaDoc i = iterator(); i.hasNext();) {
506                 ExtensionDescriptor d = (ExtensionDescriptor) i.next();
507                 try {
508                     d.activate();
509                     status = ExtensionBundleStatus.ACTIVATED;
510                 } catch (ExtensionException ex) {
511                     if (ee == null) {
512                         ee = ex;
513                     }
514                 }catch (Throwable JavaDoc t){
515                     if (ee == null) {
516                         ee = new ExtensionException(ExtensionException.INTERNAL_ERROR, t);
517                     }
518                 }
519             }
520             if (ee != null) {
521                 throw ee;
522             }
523         } catch (ExtensionException ee) {
524             log.error("Failed to activate extension bundle. ", ee);
525             error = ee;
526             status = ExtensionBundleStatus.ERROR;
527             throw ee;
528         }
529         error = null;
530     }
531
532     /**
533      * Stop all extension bundles. This method will active all extension
534      * descriptors it contains and is the second or third phase in an extensions
535      * life cycle (after starting or activating).
536      *
537      * @throws ExtensionException if any bundle could not be stopped
538      */

539     public synchronized void stop() throws ExtensionException {
540         ExtensionException ee = null;
541         try {
542             for (Iterator JavaDoc i = iterator(); i.hasNext();) {
543                 ExtensionDescriptor d = (ExtensionDescriptor) i.next();
544                 try {
545                     d.stop();
546                 } catch (ExtensionException ex) {
547                     if (ee == null) {
548                         ee = ex;
549                     }
550                     log.error("Failed to stop extension bundle. ", ex);
551                 }
552             }
553             if (ee != null) {
554                 throw ee;
555             }
556         } finally {
557             status = ExtensionBundleStatus.ENABLED;
558         }
559     }
560
561     /**
562      * Stop all extension bundles. This method will load all extension
563      * descriptors it contains and is the first phase in an extensions life
564      * cycle.
565      *
566      * @throws ExtensionException on any error loading bundles
567      */

568     public synchronized void load() throws ExtensionException {
569
570         try {
571             if (log.isInfoEnabled()) {
572                 log.info("Loading bundle from " + getFile().getAbsolutePath());
573             }
574
575             installer = new ExtensionInstaller(this);
576             SAXBuilder sax = new SAXBuilder();
577             try {
578                 doc = sax.build(descriptor);
579             } catch (JDOMException jde) {
580                 jde.printStackTrace();
581                 throw new ExtensionException(ExtensionException.FAILED_TO_PARSE_DESCRIPTOR, jde);
582             } catch (IOException JavaDoc ioe) {
583                 throw new ExtensionException(ExtensionException.INTERNAL_ERROR, ioe, "Failed to load descriptor for parsing.");
584             }
585
586             hidden = "true".equals(doc.getRootElement().getAttributeValue("hidden"));
587             license = doc.getRootElement().getAttributeValue("license");
588             license = license == null || license.equals("") ? "Unknown" : license;
589             if (log.isDebugEnabled())
590                 log.debug("Application bundle license is " + license);
591
592             productURL = doc.getRootElement().getAttributeValue("productURL");
593             instructionsURL = doc.getRootElement().getAttributeValue("instructionsURL");
594
595             // Dependencies if any
596
dependencyNames = null;
597             String JavaDoc dependencies = doc.getRootElement().getAttributeValue("dependencies");
598             if (dependencies != null) {
599                 log.warn("DEPRECATED. dependencies attribute in bundle " + getFile().getAbsolutePath()
600                     + " should now use 'depends'.");
601             } else {
602                 dependencies = doc.getRootElement().getAttributeValue("depends");
603             }
604             if (!Util.isNullOrTrimmedBlank(dependencies)) {
605                 dependencyNames = Arrays.asList(dependencies.split(","));
606             }
607
608             // Get the required host version
609
String JavaDoc requiredHostVersionString = doc.getRootElement().getAttributeValue("requiredHostVersion");
610             if (requiredHostVersionString != null && !"any".equalsIgnoreCase(requiredHostVersionString)) {
611                 requiredHostVersion = new VersionInfo.Version(requiredHostVersionString);
612                 int dif = requiredHostVersion.compareTo(VersionInfo.getVersion());
613                 if (dif > 0)
614                     throw new ExtensionException(ExtensionException.INSUFFICIENT_SSLEXPLORER_HOST_VERSION,
615                                     getName(),
616                                     requiredHostVersionString);
617
618             } else {
619                 requiredHostVersion = null;
620             }
621
622             String JavaDoc ver = doc.getRootElement().getAttributeValue("version");
623             if (ver == null || ver.equals("")) {
624                 throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
625                                 "<applications> element requires the attribute 'version'.");
626             }
627             version = new VersionInfo.Version(ver);
628
629             if (doc.getRootElement().getName().equals("bundle")) {
630
631                 Attribute a = doc.getRootElement().getAttribute("id");
632                 id = a == null ? null : a.getValue();
633
634                 if (id == null) {
635                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
636                                     "<bundle> element requires attribute 'id'");
637                 }
638                 if (log.isDebugEnabled())
639                     log.debug("Application bundle id is " + id);
640
641                 name = doc.getRootElement().getAttribute("name").getValue();
642                 if (log.isDebugEnabled())
643                     log.debug("Application bundle name is " + name);
644
645                 Attribute orderAttr = doc.getRootElement().getAttribute("order");
646                 if (orderAttr == null) {
647                     log.warn("<bundle> element in " + getFile().getPath() + " now requires attribute 'order'. Assuming 99999");
648                     order = 99999;
649                 } else {
650                     try {
651                         order = orderAttr.getIntValue();
652                     } catch (DataConversionException dce) {
653                         throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
654                                         "'order' attribute is invalid. " + dce.getMessage());
655                     }
656                 }
657
658                 licenseFilePath = doc.getRootElement().getAttributeValue("licenseAgreement");
659
660                 if (name == null) {
661                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
662                                     "<bundle> element requires the attribute 'name'");
663                 }
664                 
665                 
666
667                 for (Iterator JavaDoc i = doc.getRootElement().getChildren().iterator(); i.hasNext();) {
668                     Element e = (Element) i.next();
669                     if (e.getName().equalsIgnoreCase("description")) {
670                         description = Util.trimmedBothOrBlank(e.getText());
671                     } else if (e.getName().equalsIgnoreCase("install")) {
672                         processInstall(e);
673                     } else if (e.getName().equalsIgnoreCase("messages")) {
674                         // processed later
675
messageElement = e;
676                     } else if (e.getName().equals("application") || e.getName().equals("extension")) {
677                         ExtensionDescriptor desc = new ExtensionDescriptor();
678                         desc.load(this, e);
679                         add(desc);
680                     } else {
681                         throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
682                                         "<bundle> element may only contain <description> or <extension> (or the deprecated <application>) elements.");
683                     }
684                 }
685
686             } else if (doc.getRootElement().getName().equals("application") || doc.getRootElement().getName().equals("extension")) {
687                 log.warn("DEPRECATED. All extensions should now use the <bundle> tag, " + getFile().getPath()
688                     + " is using not using this tag.");
689                 ExtensionDescriptor desc = new ExtensionDescriptor();
690                 desc.load(this, doc.getRootElement());
691                 id = desc.getId();
692                 name = desc.getName();
693                 description = desc.getDescription();
694                 order = 99999;
695                 dependencyNames = Arrays.asList(new String JavaDoc[] { "sslexplorer-community-applications",
696                     "sslexplorer-community-tunnels" });
697                 add(desc);
698             } else {
699                 throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
700                                 "Application bundle root element must be <bundle> (or the deprecated <application> or <extension>) elements.");
701             }
702
703             // All we know is that the application is
704
// installed until the application store is
705
// available
706
setType(TYPE_INSTALLED);
707         } catch (ExtensionException ee) {
708             error = ee;
709             throw ee;
710         }
711
712         error = null;
713
714     }
715
716     /**
717      * Get if this bundle contains any plugins.
718      *
719      * @return contains plugins
720      */

721     public boolean isContainsPlugin() {
722         boolean containsPlugin = false;
723         for (ExtensionDescriptor descriptor : this) {
724             if (descriptor.getTypeName().equals(PluginType.TYPE)) {
725                 containsPlugin = true;
726             }
727         }
728         return containsPlugin;
729     }
730
731     /**
732      * Remove this bundle. If it started, then it will be stopped first. If the
733      * bundle contains plugins the actual removal of the files will be deferred
734      * until restart.
735      *
736      * @throws Exception
737      */

738     public void removeBundle() throws Exception JavaDoc {
739         if (log.isInfoEnabled())
740             log.info("Removing extension bundle " + getId());
741         boolean containsPlugin = isContainsPlugin();
742         try {
743             // Stop the bundle if no plugins are contained
744
if (!containsPlugin && getStatus().isStartedOrActivated()) {
745                 stop();
746             }
747
748             //
749
for (ExtensionDescriptor descriptor : this) {
750                 descriptor.removeDescriptor();
751             }
752         } finally {
753             if (containsPlugin) {
754                 setType(ExtensionBundle.TYPE_PENDING_REMOVAL);
755             }
756         }
757     }
758
759     /**
760      * Get the <i>Install</i> (if any) for this extension bundle.
761      * <code>null</code> will be returned if this bundle has no installer.
762      *
763      * @return extension installer
764      */

765     public ExtensionInstaller getInstaller() {
766         return installer;
767     }
768
769     public String JavaDoc getInstructionsURL() {
770         return instructionsURL;
771     }
772
773     public String JavaDoc getProductURL() {
774         return productURL;
775     }
776
777     public VersionInfo.Version getVersion() {
778         return version;
779     }
780
781     public VersionInfo.Version getDisplayVersion() {
782         return isUpdateable() ? updateVersion : version;
783     }
784
785     public String JavaDoc getDescription() {
786         return description;
787     }
788
789     public void setDescription(String JavaDoc description) {
790         this.description = description;
791     }
792
793     public void setLicense(String JavaDoc license) {
794         this.license = license;
795     }
796
797     public String JavaDoc getLicense() {
798         return license;
799     }
800
801     public int getType() {
802         return type;
803     }
804
805     public void setType(int type) {
806         this.type = type;
807     }
808
809     public File JavaDoc getBaseDir() {
810         return getFile() == null ? null : getFile().getParentFile();
811     }
812
813     /**
814      * @param application
815      * @return
816      */

817     public boolean containsApplication(String JavaDoc application) {
818         for (Iterator JavaDoc i = iterator(); i.hasNext();) {
819             if (((ExtensionDescriptor) i.next()).getId().equals(application)) {
820                 return true;
821             }
822         }
823         return false;
824     }
825
826     /**
827      * @param id
828      * @return
829      */

830     public ExtensionDescriptor getApplicationDescriptor(String JavaDoc id) {
831         for (Iterator JavaDoc i = iterator(); i.hasNext();) {
832             ExtensionDescriptor app = (ExtensionDescriptor) i.next();
833             if (app.getId().equals(id)) {
834                 return app;
835             }
836         }
837         return null;
838     }
839
840     public int compareTo(Object JavaDoc arg0) {
841         int c = getType() - ((ExtensionBundle) arg0).getType();
842         return c != 0 ? c : name.compareTo(((ExtensionBundle) arg0).name);
843     }
844
845     public File JavaDoc getLicenseFile() {
846         File JavaDoc baseDir = getBaseDir();
847         return baseDir == null || licenseFilePath == null ? null : new File JavaDoc(baseDir, licenseFilePath);
848     }
849
850     public VersionInfo.Version getRequiredHostVersion() {
851         return requiredHostVersion;
852     }
853
854     public void setRequiredHostVersion(VersionInfo.Version requiredHostVersion) {
855         this.requiredHostVersion = requiredHostVersion;
856     }
857
858     public void setCategory(String JavaDoc category) {
859         this.category = category;
860     }
861
862     public boolean isMandatoryUpdate() {
863         return mandatoryUpdate;
864     }
865
866     public Throwable JavaDoc getError() {
867         return error;
868     }
869
870     public boolean canStop() {
871         boolean canStop = status.isStartedOrActivated();
872         if (!canStop) {
873             return false;
874         }
875         for (Iterator JavaDoc i = iterator(); i.hasNext();) {
876             ExtensionDescriptor d = (ExtensionDescriptor) i.next();
877             if (!d.canStop()) {
878                 return false;
879             }
880         }
881         return true;
882
883     }
884
885     public boolean canStart() {
886         return ExtensionBundleStatus.ENABLED.equals(status) && getType() != ExtensionBundle.TYPE_PENDING_INSTALLATION
887             && getType() != ExtensionBundle.TYPE_PENDING_REMOVAL
888             && getType() != ExtensionBundle.TYPE_PENDING_UPDATE
889             && getType() != ExtensionBundle.TYPE_PENDING_STATE_CHANGE;
890     }
891
892     public boolean canDisable() {
893         return !ExtensionBundleStatus.DISABLED.equals(status) && type != TYPE_CONFIGUREABLE && type != TYPE_INSTALLABLE && type != TYPE_PENDING_STATE_CHANGE;
894     }
895
896     public boolean canEnable() {
897         return ExtensionBundleStatus.DISABLED.equals(status) && type != TYPE_PENDING_STATE_CHANGE;
898     }
899
900     public ExtensionBundleStatus getStatus() {
901         return status;
902     }
903
904     public void setStatus(ExtensionBundleStatus status) {
905         this.status = status;
906     }
907
908     private void processInstall(Element installElement) throws ExtensionException {
909         String JavaDoc when = installElement.getAttributeValue("when");
910         when = when == null ? ExtensionInstaller.ON_ACTIVATE : when;
911         for (Iterator JavaDoc i = installElement.getChildren().iterator(); i.hasNext();) {
912             Element e = (Element) i.next();
913             if (e.getName().equalsIgnoreCase("mkdir")) {
914                 String JavaDoc dir = Util.trimmedBothOrBlank(e.getText());
915                 if (dir == null || dir.equals("")) {
916                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
917                                     "<mkdir> contents must be the name of a directory to create.");
918                 }
919                 installer.addOp(new ExtensionInstaller.MkdirInstallOp(when, dir));
920             } else if (e.getName().equalsIgnoreCase("cp")) {
921                 String JavaDoc from = Util.trimmedBothOrBlank(e.getText());
922                 String JavaDoc to = e.getAttributeValue("to");
923                 String JavaDoc toDir = e.getAttributeValue("toDir");
924                 boolean overwrite = "true".equalsIgnoreCase(e.getAttributeValue("overwrite"));
925                 if (from == null || from.equals("") || ((to == null || to.equals("")) && (toDir == null || toDir.equals("")))) {
926                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
927                                     "<cp> content must be the source path and the tag must have either a to or toDir attribute.");
928                 }
929                 installer.addOp(new ExtensionInstaller.CpInstallOp(when, from, to, toDir, overwrite));
930             } else if (e.getName().equalsIgnoreCase("rm")) {
931                 String JavaDoc path = Util.trimmedBothOrBlank(e.getText());
932                 if (path == null || path.equals("")) {
933                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
934                                     "<rm> must a path as its content.");
935                 }
936                 installer.addOp(new ExtensionInstaller.RmInstallOp(when, path));
937             } else if (e.getName().equalsIgnoreCase("custom")) {
938                 String JavaDoc clazz = Util.trimmedBothOrBlank(e.getText());
939                 if (clazz == null || clazz.equals("")) {
940                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
941                                     "<custom> must provide a class name that implements ExtensionInstaller.ExtensionInstallOp as its content.");
942                 }
943                 try {
944                     installer.addOp(new ExtensionInstaller.CustomInstallOpWrapper(when, clazz));
945                 } catch (Exception JavaDoc ex) {
946                     throw new ExtensionException(ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
947                                     ex, "Failed to create <custom> install op.");
948
949                 }
950             }
951         }
952     }
953
954     public String JavaDoc getChanges() {
955         return changes==null ? "" : changes.trim();
956     }
957     
958     public String JavaDoc toString() {
959         return id + " " + version;
960     }
961
962     public VersionInfo.Version getUpdateVersion() {
963         return updateVersion;
964     }
965
966     public void setUpdateVersion(VersionInfo.Version updateVersion) {
967         this.updateVersion = updateVersion;
968     }
969
970     public void setChanges(String JavaDoc changes) {
971         this.changes = changes;
972     }
973 }
Popular Tags