KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > extensions > store > ExtensionStore


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.store;
21
22 import java.io.File JavaDoc;
23 import java.io.FileFilter JavaDoc;
24 import java.io.FilenameFilter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.net.MalformedURLException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.net.URLConnection JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Calendar JavaDoc;
33 import java.util.Collection JavaDoc;
34 import java.util.Collections JavaDoc;
35 import java.util.Comparator JavaDoc;
36 import java.util.GregorianCalendar JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.StringTokenizer JavaDoc;
42 import java.util.prefs.BackingStoreException JavaDoc;
43 import java.util.prefs.Preferences JavaDoc;
44
45 import javax.servlet.http.HttpServletRequest JavaDoc;
46
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49 import org.apache.struts.action.ActionForward;
50 import org.jdom.JDOMException;
51
52 import com.sslexplorer.boot.Context;
53 import com.sslexplorer.boot.ContextHolder;
54 import com.sslexplorer.boot.RepositoryFactory;
55 import com.sslexplorer.boot.RepositoryStore;
56 import com.sslexplorer.boot.Util;
57 import com.sslexplorer.boot.VersionInfo;
58 import com.sslexplorer.core.BundleActionMessage;
59 import com.sslexplorer.core.CoreAttributeConstants;
60 import com.sslexplorer.core.CoreEvent;
61 import com.sslexplorer.core.CoreEventConstants;
62 import com.sslexplorer.core.CoreMessageResources;
63 import com.sslexplorer.core.CoreServlet;
64 import com.sslexplorer.core.CoreUtil;
65 import com.sslexplorer.core.GlobalWarning;
66 import com.sslexplorer.core.LicenseAgreement;
67 import com.sslexplorer.extensions.ExtensionBundle;
68 import com.sslexplorer.extensions.ExtensionDescriptor;
69 import com.sslexplorer.extensions.ExtensionException;
70 import com.sslexplorer.extensions.ExtensionInstaller;
71 import com.sslexplorer.extensions.ExtensionType;
72 import com.sslexplorer.extensions.ExtensionBundle.ExtensionBundleStatus;
73 import com.sslexplorer.setup.LicenseAgreementCallback;
74 import com.sslexplorer.tasks.Task;
75 import com.sslexplorer.tasks.TaskHttpServletRequest;
76 import com.sslexplorer.tasks.TaskInputStream;
77 import com.sslexplorer.tasks.TaskProgressBar;
78 import com.sslexplorer.util.ZipExtract;
79
80 /**
81  * Manages all aspectes of <i>Extensions</i> including loading, starting,
82  * activating, disabling, installing, remove and updating.
83  * <p>
84  * This class also manages the interaction with the <i>3SP Extension Store</i>,
85  * including update checking and installing from the store.
86  *
87  * @author Lee David Painter <a HREF="mailto:lee@3sp.com">&lt;lee@3sp.com&gt;</a>
88  * @author Brett Smith <a HREF="mailto:brett@3sp.com">&lt;brett@3sp.com&gt;</a>
89  */

90 public class ExtensionStore {
91
92     public static final String JavaDoc HTTP_3SP_COM_APPSTORE = "http://download.3sp.com/appstore/";
93     private static final Log log = LogFactory.getLog(ExtensionStore.class);
94     private static final String JavaDoc DIRS_TO_REMOVE = "dirsToRemove";
95     private static String JavaDoc currentEdition = "Community Edition";
96
97     public static final String JavaDoc UPDATEABLE = "Updateable";
98
99     /**
100      * Name of store in repository for extension bundle archives
101      */

102     public static final String JavaDoc ARCHIVE_STORE = "archives";
103
104     /**
105      * 'Installed' category in extension manager
106      */

107     public static final String JavaDoc INSTALLED_CATEGORY = "Installed";
108
109     /**
110      * The extension bundle id for the Agent.
111      */

112     public static final String JavaDoc AGENT_EXTENSION_BUNDLE_ID = "sslexplorer-agent";
113
114     /**
115      * Preferences node for storing current extension version and other
116      * extension related details
117      */

118     public static final Preferences JavaDoc PREFS = ContextHolder.getContext().getPreferences().node("extensions");
119
120     /**
121      * Preferences node with the extensions node for storing extension store
122      * related details.
123      */

124     public static final Preferences JavaDoc STORE_PREF = PREFS.node("store");
125
126     /**
127      * Preferences node with the extensions node for storing extension version
128      * details.
129      */

130     public static final Preferences JavaDoc VERSION_PREFS = PREFS.node("versions");
131
132     /**
133      * Extension store connect timeout
134      */

135     public static final int CONNECT_TIMEOUT = 30000;
136
137     /**
138      * Extension store read timeout
139      */

140     public static final int READ_TIMEOUT = 30000;
141
142     // Private instance variables
143

144     private File JavaDoc basedir;
145     private Map JavaDoc<String JavaDoc, ExtensionBundle> extensionBundles;
146     private List JavaDoc<ExtensionBundle> extensionBundlesList;
147     private List JavaDoc<ExtensionInstaller> extensionBundleInstallList;
148     private ExtensionStoreDescriptor downloadableExtensions;
149     private Calendar JavaDoc downloadableExtensionsLastUpdated;
150     private boolean repositoryBacked;
151     private static ExtensionStore instance;
152     //private UpdateChecker updateChecker;
153

154     /**
155      * Get an instance of the extension store.
156      *
157      * @return instance
158      */

159     public static ExtensionStore getInstance() {
160         if (instance == null) {
161             instance = new ExtensionStore();
162         }
163         return instance;
164     }
165
166     /**
167      * Get the update checker that checks for update to the core, extensions and
168      * loads the RSS feeds
169      *
170      * @return update checker
171      */

172     //public UpdateChecker getUpdateChecker() {
173
// return updateChecker;
174
//}
175

176     /**
177      * Get the directory where expanded extensions are stored.
178      *
179      * @return the extension store directory
180      */

181     public File JavaDoc getExtensionStoreDirectory() {
182         return basedir;
183     }
184
185     /**
186      * Get if the extension store is 'Repository Backed'. See class description
187      * for details.
188      *
189      * @return true if it is repository backed
190      */

191     public boolean isRepositoryBacked() {
192         return repositoryBacked;
193     }
194
195     /**
196      * Initialise the extension store.
197      *
198      * @param basedir
199      * @throws IOException
200      */

201     public void init(File JavaDoc basedir) throws IOException JavaDoc {
202
203         //updateChecker = new UpdateChecker();
204

205         // Get if the application store comes from the repository
206
repositoryBacked = "true".equals(System.getProperty("sslexplorer.extensions.repositoryBacked", "true"));
207
208         this.basedir = basedir;
209
210         extensionBundles = new HashMap JavaDoc<String JavaDoc, ExtensionBundle>();
211         extensionBundlesList = new ArrayList JavaDoc<ExtensionBundle>();
212         extensionBundleInstallList = new ArrayList JavaDoc<ExtensionInstaller>();
213
214         if (isRepositoryBacked()) {
215             initialiseRepository();
216         }
217
218         try {
219             loadAll();
220             // TODO display errors to use somehow
221
} catch (Exception JavaDoc e) {
222             log.error("Failed extract extension bundles from repository.", e);
223         }
224
225         /*
226          * A lot of plugins were made incompatible at 0.2.10. Here we make sure
227          * any incompatible extensions are disabled
228          */

229         VersionInfo.Version sslxVersion = ContextHolder.getContext().getVersion();
230         if (sslxVersion.getMajor() == 0 && sslxVersion.getMinor() == 2 && sslxVersion.getBuild() == 10) {
231             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
232             for (ExtensionBundle bundle : extensionBundlesList) {
233                 if (bundle.getRequiredHostVersion() == null || bundle.getRequiredHostVersion().compareTo(sslxVersion) < 0) {
234                     log.warn("Extension " + bundle.getId()
235                         + " has a required host version of "
236                         + bundle.getRequiredHostVersion()
237                         + " where as "
238                         + "this version of SSL-Explorer is "
239                         + sslxVersion
240                         + ". This plugin will be disabled.");
241                     ExtensionStoreStatusManager.systemDisableExtension(bundle.getId());
242                     if (buf.length() > 0) {
243                         buf.append(",");
244                     }
245                     buf.append(bundle.getName());
246                 }
247             }
248             if (buf.length() > 0) {
249                 CoreUtil.addMultipleGlobalWarning(GlobalWarning.MANAGEMENT_USERS, new BundleActionMessage("extensions",
250                                 "startup.disabledExtensions",
251                                 buf.toString()));
252             }
253         }
254
255         /*
256          * First remove any plugins whose uninstallation may have been deferred
257          * until restart
258          */

259         if (!isRepositoryBacked()) {
260
261             String JavaDoc dirsToRemove = STORE_PREF.get(DIRS_TO_REMOVE, "");
262             if (!dirsToRemove.equals("")) {
263                 StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(dirsToRemove, ",");
264                 while (t.hasMoreTokens()) {
265                     File JavaDoc dir = new File JavaDoc(t.nextToken());
266                     if (dir.exists()) {
267                         if (log.isInfoEnabled())
268                             log.info("Removing extension " + dir.getAbsolutePath());
269                         Util.delTree(dir);
270                     }
271                 }
272                 STORE_PREF.remove(DIRS_TO_REMOVE);
273             }
274
275             /*
276              * Check for extension updates
277              */

278             File JavaDoc updatedExtensionsDir = getUpdatedExtensionsDirectory();
279             File JavaDoc[] extensions = updatedExtensionsDir.listFiles();
280             if (extensions != null) {
281                 for (int i = 0; i < extensions.length; i++) {
282                     File JavaDoc destDir = new File JavaDoc(ContextHolder.getContext().getApplicationDirectory(), extensions[i].getName());
283                     if (destDir.exists()) {
284                         if (log.isInfoEnabled())
285                             log.info("Removing extension " + destDir.getAbsolutePath());
286                         if (!Util.delTree(destDir)) {
287                             throw new IOException JavaDoc("Failed to remove old extension " + destDir.getAbsolutePath());
288                         }
289                     }
290                     if (log.isInfoEnabled())
291                         log.info("Moving " + extensions[i].getAbsolutePath() + " to " + destDir.getAbsolutePath());
292                     if (!extensions[i].renameTo(destDir)) {
293                         throw new IOException JavaDoc("Failed to rename extension " + extensions[i].getAbsolutePath()
294                             + " to "
295                             + destDir.getAbsolutePath());
296                     }
297                 }
298             }
299         }
300
301         // Add any additional class path elements
302
addAdditionalClasspath();
303         addAdditionalWebResource();
304
305         // Check for any mandatory updates
306
//try {
307
// updateChecker.initialise();
308
//} catch (Exception e) {
309
/*
310              * There is no need to prevent start up if we fail to get the
311              * available versions.
312              */

313         // log.error("Failed to check for any extension updates.", e);
314
//}
315
}
316
317     /**
318      * @return the available extension bundles
319      */

320     @SuppressWarnings JavaDoc("unchecked")
321     public List JavaDoc<ExtensionBundle> getAllAvailableExtensionBundles() {
322         List JavaDoc<ExtensionBundle> all = new ArrayList JavaDoc<ExtensionBundle>(extensionBundlesList);
323         try {
324             ExtensionStoreDescriptor descriptor = getDownloadableExtensionStoreDescriptor(downloadableExtensions != null, getWorkingVersion());
325             if (descriptor != null && descriptor.getExtensionBundles() != null) {
326                 for (Iterator JavaDoc itr = descriptor.getExtensionBundles().iterator(); itr.hasNext();) {
327                     ExtensionBundle bundle = (ExtensionBundle) itr.next();
328                     // If the app is already installed, remove dont include it
329
// in the list
330
if (!extensionBundles.containsKey(bundle.getId())) {
331                         all.add(bundle);
332                     }
333                 }
334             }
335         } catch (Exception JavaDoc e) {
336             log.error("Failed to get downloadable extensions.", e);
337         }
338         Collections.sort(all);
339         return all;
340     }
341
342     /**
343      * Get all the available extension bundles fro a given category.
344      *
345      * @param category
346      * @return the available extension bundles
347      */

348     @SuppressWarnings JavaDoc("unchecked")
349     public List JavaDoc<ExtensionBundle> getAllAvailableExtensionBundles(String JavaDoc category) {
350         // just get the installed ones.
351
if (category.equals(INSTALLED_CATEGORY)){
352             return extensionBundlesList;
353         }
354         
355         List JavaDoc<ExtensionBundle> all = new ArrayList JavaDoc<ExtensionBundle>();
356         if (category.equals(UPDATEABLE)){
357             for (ExtensionBundle bundle : extensionBundlesList) {
358                 // add all the updateable extensions
359
if (bundle.isUpdateable()){
360                     all.add(bundle);
361                 }
362             }
363         }
364         
365         try {
366             ExtensionStoreDescriptor descriptor = getDownloadableExtensionStoreDescriptor(downloadableExtensions != null, getWorkingVersion());
367             if (descriptor != null && descriptor.getExtensionBundles() != null) {
368                 for (Iterator JavaDoc itr = descriptor.getExtensionBundles().iterator(); itr.hasNext();) {
369                     ExtensionBundle bundle = (ExtensionBundle) itr.next();
370                     // If the app is already installed, remove dont include it
371
// in the list
372
if (!extensionBundles.containsKey(bundle.getId()) && bundle.getCategory().equals(category)) {
373                         all.add(bundle);
374                     }
375                 }
376             }
377         } catch (Exception JavaDoc e) {
378             log.error("Failed to get downloadable extensions.", e);
379         }
380         Collections.sort(all);
381         return all;
382     }
383     
384     /**
385      * @param id
386      * @param version
387      * @return URLConnection
388      * @throws IOException
389      */

390     public URLConnection JavaDoc downloadExtension(String JavaDoc id, String JavaDoc version) throws IOException JavaDoc {
391         URL JavaDoc downloadURL = getDownloadURL(id, version);
392         if (downloadURL != null) {
393             if (log.isInfoEnabled())
394                 log.info("Downloading extension from " + downloadURL.toExternalForm());
395             URLConnection JavaDoc con = downloadURL.openConnection();
396             con.setConnectTimeout(CONNECT_TIMEOUT);
397             con.setReadTimeout(READ_TIMEOUT);
398             con.connect();
399             return con;
400         } else {
401             throw new IOException JavaDoc("No valid download location for " + id);
402         }
403     }
404
405     /**
406      * Start all extensions bundles
407      *
408      * @throws ExtensionException any error starting a bundle. If multiple
409      * extensions are started then only the first exception thrown by
410      * the bundle will be thrown from this method, an attempt will be
411      * made to start all other extensions bundles.
412      */

413     public void start() throws ExtensionException {
414         if (log.isInfoEnabled())
415             log.info("Starting extension store. Extensions will start in the following order .. ");
416         Collections.sort(extensionBundlesList, new BundleComparator());
417         for (ExtensionBundle bundle : extensionBundlesList) {
418             log.info(" " + bundle.getId() + " (" + bundle.getOrder() + ")");
419         }
420
421         for (ExtensionBundle bundle : extensionBundlesList) {
422
423             ContextHolder.getContext().getBootProgressMonitor().updateMessage("Starting " + bundle.getName());
424             ContextHolder.getContext()
425                             .getBootProgressMonitor()
426                             .updateProgress((int) (30 + (10 * ((float) extensionBundlesList.indexOf(bundle) / extensionBundlesList.size()))));
427
428             // Start the bundle
429
try {
430                 if (bundle.getStatus() == ExtensionBundleStatus.ENABLED) {
431                     bundle.start();
432                 }
433             } catch (Throwable JavaDoc t) {
434                 /*
435                  * Catch throwable to prevent bad extensions from interferring
436                  * with the core (e.g. NoClassDefFoundError,
437                  * ClassNotFoundException)
438                  */

439                 log.error("Failed to start extension bundle.", t);
440             }
441         }
442
443         /*
444          * First check which extensions should have their installers run
445          */

446         checkExtensionsForInstallation();
447
448         /*
449          * Now run the installer for the start phase
450          */

451         performInstalls(ExtensionInstaller.ON_START);
452
453     }
454
455     /**
456      * Stop all extensions bundles
457      *
458      * @throws ExtensionException any error stopping a bundle. If multiple
459      * extensions are started then only the first exception thrown by
460      * the bundle will be thrown from this method, an attempt will be
461      * made to start all other extensions bundles.
462      */

463     public void stop() throws ExtensionException {
464         if (log.isInfoEnabled())
465             log.info("Stopping extensions");
466
467         // ensure all threads are finished.
468
//updateChecker.end();
469

470         // Stop extensions in the reverse order they were started
471
Collections.reverse(extensionBundlesList);
472
473         ExtensionException ee = null;
474         for (ExtensionBundle bundle : extensionBundlesList) {
475             try {
476                 bundle.stop();
477             } catch (ExtensionException e) {
478                 if (ee == null) {
479                     ee = e;
480                 }
481                 log.error("Failed to stop extension bundle.", ee);
482             }
483         }
484         if (ee != null) {
485             throw ee;
486         }
487     }
488
489     /**
490      * Activate all extensions bundles
491      *
492      * @throws ExtensionException any error activating a bundle. If multiple
493      * extensions are started then only the first exception thrown by
494      * the bundle will be thrown from this method, an attempt will be
495      * made to start all other extensions bundles.
496      */

497     public void activate() throws ExtensionException {
498         if (log.isInfoEnabled())
499             log.info("Activating extension store.");
500
501         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
502         for (ExtensionBundle bundle : extensionBundlesList) {
503             try {
504                 if (bundle.getStatus() == ExtensionBundleStatus.STARTED) {
505
506                     ContextHolder.getContext().getBootProgressMonitor().updateMessage("Activating " + bundle.getName());
507                     ContextHolder.getContext()
508                                     .getBootProgressMonitor()
509                                     .updateProgress((int) (65 + (10 * ((float) extensionBundlesList.indexOf(bundle) / extensionBundlesList.size()))));
510
511                     bundle.activate();
512                     
513                     if(buf.length() != 0)
514                         buf.append(",");
515                     buf.append(bundle.getId());
516                 }
517             } catch (Throwable JavaDoc t) {
518                 /*
519                  * Catch throwable to prevent bad extensions from interferring
520                  * with the core (e.g. NoClassDefFoundError,
521                  * ClassNotFoundException)
522                  */

523                 log.error("Failed to activate extension bundle.", t);
524             }
525         }
526
527         /*
528          * Remove the versions of any extensions that no longer exist
529          */

530         String JavaDoc[] k;
531         try {
532             k = VERSION_PREFS.keys();
533             for (int i = 0; i < k.length; i++) {
534                 if (!isExtensionBundleLoaded(k[i])) {
535                     VERSION_PREFS.remove(k[i]);
536                 }
537             }
538         } catch (BackingStoreException JavaDoc e) {
539             log.warn("Could not clean up extension versions preferences node.", e);
540         }
541
542         // Start watching for version updates and RSS updates (if enabled)
543
//updateChecker.start();
544

545         /*
546          * Now run the installer for the start phase
547          */

548         performInstalls(ExtensionInstaller.ON_ACTIVATE);
549         
550         /**
551          * If activat plugins has changed since the last full activation
552          * then clear out the compiled JSP files
553          */

554         if(!buf.toString().equals(PREFS.get("lastActivatedPlugins", ""))) {
555             Util.delTree(new File JavaDoc(ContextHolder.getContext().getTempDirectory(), "org"));
556         }
557         PREFS.put("lastActivatedPlugins", buf.toString());
558
559         /* Flush these preferences now incase the server is terminated before
560          * it gets a chance to write the preferences. When running in a development,
561          * its possible for the server to get confused about when it should
562          * clear out the temporary temporary directory.
563          */

564         try {
565             PREFS.flush();
566         }
567         catch(BackingStoreException JavaDoc bse) {
568         }
569     }
570
571     /**
572      * reset
573      */

574     public void resetExtensionStoreUpdate() {
575         downloadableExtensions = null;
576     }
577
578     /**
579      * @param connect
580      * @return ExtensionStoreDescriptor
581      * @throws IOException
582      * @throws JDOMException
583      */

584     public ExtensionStoreDescriptor getDownloadableExtensionStoreDescriptor(boolean connect) throws IOException JavaDoc, JDOMException {
585         return getDownloadableExtensionStoreDescriptor(connect, getWorkingVersion());
586     }
587     
588     /**
589      * @param connect
590      * @param version
591      * @return ExtensionStoreDescriptor
592      * @throws IOException
593      * @throws JDOMException
594      */

595     public ExtensionStoreDescriptor getDownloadableExtensionStoreDescriptor(boolean connect, VersionInfo.Version version) throws IOException JavaDoc, JDOMException {
596         if (downloadableExtensions != null && downloadableExtensionsLastUpdated != null) {
597             Calendar JavaDoc calendar = ((Calendar JavaDoc) downloadableExtensionsLastUpdated.clone());
598             calendar.add(Calendar.DAY_OF_MONTH, 1);
599             if (new GregorianCalendar JavaDoc().after(calendar)) {
600                 if (log.isInfoEnabled())
601                     log.info("Downloadable extensions are out of date, will contact the update site again.");
602                 downloadableExtensions = null;
603             }
604         }
605
606         if (downloadableExtensions == null && connect) {
607             URL JavaDoc storeURL = getStoreDownloadURL(ExtensionStore.HTTP_3SP_COM_APPSTORE, version);
608             if (storeURL != null) {
609                 if (log.isInfoEnabled())
610                     log.info("Loading extension store descriptor from " + storeURL.getHost());
611
612                 downloadableExtensions = new ExtensionStoreDescriptor(storeURL);
613                 downloadableExtensionsLastUpdated = new GregorianCalendar JavaDoc();
614
615                 for (ExtensionBundle extensionBundle : downloadableExtensions.getExtensionBundles()) {
616                     try {
617                         ExtensionBundle installedApp = getExtensionBundle(extensionBundle.getId());
618                         if (!installedApp.isDevExtension() && installedApp.getVersion().compareTo(extensionBundle.getVersion()) < 0) {
619                             if (log.isInfoEnabled())
620                                 log.info("Update found for extenions " + extensionBundle.getId());
621                             installedApp.setType(ExtensionBundle.TYPE_UPDATEABLE);
622                             installedApp.setUpdateVersion(extensionBundle.getVersion());
623                             installedApp.setChanges(extensionBundle.getChanges());
624                         }
625                     } catch (Exception JavaDoc e) {
626                         // Not installed
627
}
628                 }
629
630                 if (log.isInfoEnabled())
631                     log.info("Extension store descriptor loaded from " + storeURL.getHost());
632             }
633         }
634         return downloadableExtensions;
635     }
636
637     void performInstalls(String JavaDoc phase) {
638         for (ExtensionInstaller installer : extensionBundleInstallList) {
639             try {
640                 if (log.isInfoEnabled()) {
641                     log.info("Performing installer for " + installer.getBundle().getName() + " phase " + phase);
642                 }
643                 installer.doInstall(phase);
644             } catch (Exception JavaDoc e) {
645                 log.warn("Installer for " + installer.getBundle().getName() + " phase " + phase + " failed.", e);
646             }
647         }
648     }
649
650     void checkExtensionsForInstallation() {
651         extensionBundleInstallList.clear();
652         for (ExtensionBundle bundle : extensionBundlesList) {
653             if (bundle.getInstaller().getOpCount() > 0) {
654                 String JavaDoc ver = VERSION_PREFS.get(bundle.getId(), "");
655                 if (ver.equals("") || !ver.equals(bundle.getVersion().toString())) {
656                     if (ver.equals("")) {
657                         log.info("Will run installer for " + bundle.getId() + " because this is its first install");
658                     } else {
659                         log.info("Will run installer for " + bundle.getId()
660                             + " because the last installed version "
661                             + ver
662                             + " has been upgraded to "
663                             + bundle.getVersion().toString());
664                     }
665                     extensionBundleInstallList.add(bundle.getInstaller());
666                 }
667             }
668         }
669     }
670
671     private static URL JavaDoc getDownloadURL(String JavaDoc id, String JavaDoc version) {
672         try {
673             String JavaDoc location = System.getProperty("sslexplorer.downloadableApplicationStore.location", ExtensionStore.HTTP_3SP_COM_APPSTORE);
674             location += Util.urlEncode(id) + "/" + Util.urlEncode(version) + "/" + Util.urlEncode(id) + ".zip";
675             return new URL JavaDoc(location);
676         } catch (MalformedURLException JavaDoc murle) {
677             try {
678                 String JavaDoc path = System.getProperty("sslexplorer.downloadableApplications.location");
679                 path = path.replaceAll("\\$\\{id\\}", id);
680                 path = path.replaceAll("\\$\\{version\\}", version);
681                 return new File JavaDoc(path).toURL();
682             } catch (MalformedURLException JavaDoc e) {
683                 log.error("Invalid downloadable extension location specified in system property sslexplorer.downloadableApplicationStore.location, '" + System.getProperty("sslexplorer.downloadableApplicationStore.location")
684                     + "'. Must be either a URL or the file path of the store descriptor file.");
685             }
686         }
687         return null;
688     }
689
690     public static URL JavaDoc getStoreDownloadURL(String JavaDoc appStoreLocation, VersionInfo.Version version) {
691         try {
692             String JavaDoc location = System.getProperty("sslexplorer.downloadableApplicationStore.location", appStoreLocation);
693             location += "core/" + Util.urlEncode(version.toString()) + "/store.xml";
694             return new URL JavaDoc(location);
695         } catch (MalformedURLException JavaDoc murle) {
696             try {
697                 return new File JavaDoc(System.getProperty("sslexplorer.downloadableApplicationStore.location")).toURL();
698             } catch (MalformedURLException JavaDoc e) {
699                 log.error("Invalid downloadable extension store location specified in system property sslexplorer.downloadableApplicationStore.location, '" + System.getProperty("sslexplorer.downloadableApplicationStore.location")
700                     + "'. Must be either a URL or the file path of the store descriptor file.");
701             }
702         }
703         return null;
704     }
705
706     public static URL JavaDoc getStoreDownloadURL(VersionInfo.Version version) {
707         return ExtensionStore.getStoreDownloadURL(ExtensionStore.HTTP_3SP_COM_APPSTORE, version);
708     }
709     
710     /**
711      *
712      */

713     public void deregisterApplicationPermissions() {
714         Util.toDo("Deregister application permissions");
715     }
716
717     private synchronized void reloadAll() throws Exception JavaDoc {
718         CoreMessageResources resources = CoreServlet.getServlet().getExtensionStoreResources();
719         for (ExtensionBundle extensionBundle : extensionBundles.values()) {
720             for (ExtensionDescriptor descriptor : extensionBundle) {
721                 Collection JavaDoc<String JavaDoc> toRemove = new ArrayList JavaDoc<String JavaDoc>();
722
723                 for (Iterator JavaDoc itr = resources.keys(); itr.hasNext();) {
724                     String JavaDoc key = (String JavaDoc) itr.next();
725                     if (key.startsWith("application." + descriptor.getId() + ".")) {
726                         toRemove.add(key);
727                     }
728                 }
729
730                 for (Iterator JavaDoc itr = toRemove.iterator(); itr.hasNext();) {
731                     resources.removeKey((String JavaDoc) itr.next());
732                 }
733             }
734         }
735
736         extensionBundles.clear();
737         extensionBundlesList.clear();
738
739         loadAll();
740     }
741
742     @SuppressWarnings JavaDoc("unchecked")
743     private void loadAll() throws Exception JavaDoc {
744
745         if (log.isInfoEnabled())
746             log.info("Loading applications");
747
748         if (!basedir.exists()) {
749             basedir.mkdirs();
750         }
751
752         // Load dev extensions first
753
loadDevExtensions();
754
755         File JavaDoc[] files = basedir.listFiles();
756         for (int index = 0; index < files.length; index++) {
757             try {
758                 loadDir(files[index]);
759             } catch (Exception JavaDoc e) {
760                 log.error("Failed to load " + files[index].getName(), e);
761             }
762         }
763
764         String JavaDoc descriptors = System.getProperty("sslexplorer.additionalDescriptors", "");
765         // Load any additional descriptors
766
for (StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(descriptors, ","); tokenizer.hasMoreTokens();) {
767             File JavaDoc file = new File JavaDoc(tokenizer.nextToken());
768             if (file.exists()) {
769                 try {
770                     loadBundle(file, false);
771                 } catch (Exception JavaDoc e) {
772                     log.error("Failed to load " + file.getAbsolutePath(), e);
773                 }
774             }
775         }
776
777         Collections.sort(extensionBundlesList);
778     }
779
780     private void loadDir(File JavaDoc dir) throws ExtensionException {
781         if (dir.isDirectory()) {
782             File JavaDoc[] descriptors = dir.listFiles(new FilenameFilter JavaDoc() {
783                 public boolean accept(File JavaDoc f, String JavaDoc filename) {
784                     return filename.equals("application.xml") || filename.equals("extension.xml");
785                 }
786             });
787
788             if (descriptors.length == 0) {
789                 log.warn("Extension folder " + dir.getName() + " found with no extension.xml (or the deprecated application.xml)");
790                 return;
791             } else if (descriptors.length > 1) {
792                 // Should never happen if its case sensitive
793
log.warn("Extension folder " + dir.getName()
794                     + " found with too many extension.xml (or deprecated application.xml) files. Please remove one. This extensions will be ignored.");
795                 return;
796             }
797             if (log.isInfoEnabled())
798                 log.info("Found application bundle " + dir.getName());
799
800             if (descriptors[0].getName().equals("application.xml")) {
801                 log.warn("DEPRECATED. Application descriptor file " + descriptors[0]
802                     + " is no longer used, please use extension.xml instead.");
803             }
804             loadBundle(descriptors[0], false);
805         }
806     }
807
808     private void loadBundle(File JavaDoc descriptor, boolean devExtension) throws ExtensionException {
809         ExtensionBundle bundle = new ExtensionBundle(descriptor, devExtension);
810         loadBundle(bundle);
811     }
812
813     private void loadBundle(ExtensionBundle bundle) throws ExtensionException {
814
815         bundle.load();
816         ExtensionBundle oldBundle = (ExtensionBundle) extensionBundles.get(bundle.getId());
817
818         if (oldBundle != null && oldBundle.isDevExtension()) {
819             throw new ExtensionException(ExtensionException.CANNOT_REPLACE_DEV_EXTENSION, bundle.getId());
820         }
821
822         bundle.setCategory(ExtensionStore.INSTALLED_CATEGORY);
823         try {
824             ExtensionBundleStatus extensionStatus = ExtensionStoreStatusManager.getExtensionStatus(bundle.getId());
825             bundle.setStatus(extensionStatus);
826         } catch (IOException JavaDoc ioe) {
827             throw new ExtensionException(ExtensionException.INTERNAL_ERROR, ioe, "Failed to add bundle.");
828         }
829
830         for (ExtensionDescriptor descriptor : bundle) {
831             if (log.isInfoEnabled())
832                 log.info("Extension " + descriptor.getName() + " has been loaded");
833         }
834         extensionBundlesList.remove(oldBundle);
835         extensionBundles.put(bundle.getId(), bundle);
836         extensionBundlesList.add(bundle);
837     }
838
839     /**
840      * @param bundleId
841      * @throws ExtensionException
842      * @throws IOException
843      */

844     public void systemDisableExtension(String JavaDoc bundleId) throws ExtensionException, IOException JavaDoc {
845         ExtensionBundle extensionBundle = getExtensionBundle(bundleId);
846         extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.SYSTEM_DISABLED);
847         ExtensionStoreStatusManager.systemDisableExtension(bundleId);
848     }
849
850     /**
851      * @param bundleId
852      * @throws Exception
853      */

854     public void disableExtension(String JavaDoc bundleId) throws Exception JavaDoc {
855         ExtensionBundle extensionBundle = getExtensionBundle(bundleId);
856         extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.DISABLED);
857         ExtensionStoreStatusManager.disableExtension(bundleId);
858         if(extensionBundle.isContainsPlugin()) {
859             extensionBundle.setType(ExtensionBundle.TYPE_PENDING_STATE_CHANGE);
860         }
861     }
862
863     /**
864      * @param bundleId
865      * @throws Exception
866      */

867     public void enableExtension(String JavaDoc bundleId) throws Exception JavaDoc {
868         ExtensionBundle extensionBundle = getExtensionBundle(bundleId);
869         extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.ENABLED);
870         ExtensionStoreStatusManager.enableExtension(bundleId);
871         if(extensionBundle.isContainsPlugin()) {
872             extensionBundle.setType(ExtensionBundle.TYPE_PENDING_STATE_CHANGE);
873         }
874     }
875
876     private static void installExtension(ExtensionBundle extensionBundle) throws IOException JavaDoc {
877         if (ExtensionBundle.ExtensionBundleStatus.SYSTEM_DISABLED.equals(extensionBundle.getStatus())) {
878             ExtensionStoreStatusManager.installExtension(extensionBundle.getId());
879             extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.ENABLED);
880         }
881     }
882
883     /**
884      * @return List
885      */

886     public List JavaDoc<ExtensionBundle> getExtensionBundles() {
887         return extensionBundlesList;
888     }
889
890     /**
891      * @param name
892      * @return true if the extension bundle has been loaded
893      */

894     public boolean isExtensionBundleLoaded(String JavaDoc name) {
895         return extensionBundles.containsKey(name);
896     }
897
898     /**
899      * @return ExtensionDescriptor
900      */

901     public ExtensionDescriptor getAgentApplication() {
902         try {
903             ExtensionBundle bundle = getExtensionBundle(AGENT_EXTENSION_BUNDLE_ID);
904             return bundle != null ? (ExtensionDescriptor) bundle.getApplicationDescriptor(AGENT_EXTENSION_BUNDLE_ID) : null;
905         } catch (Exception JavaDoc e) {
906             log.error("Failed to get agent descriptor. Loaded?", e);
907             return null;
908         }
909     }
910
911     /**
912      * @param id
913      * @return ExtensionDescriptor
914      * @throws Exception
915      */

916     public ExtensionDescriptor getExtensionDescriptor(String JavaDoc id) throws Exception JavaDoc {
917         for (ExtensionBundle bundle : extensionBundlesList) {
918             ExtensionDescriptor descriptor = bundle.getApplicationDescriptor(id);
919             if (descriptor != null && descriptor instanceof ExtensionDescriptor) {
920                 return (ExtensionDescriptor) descriptor;
921             }
922         }
923         return null;
924     }
925
926     /**
927      * Get an extension bundle given its ID.
928      *
929      * @param id extension bundle id
930      * @return extension bundle
931      * @throws ExtensionException if bundle could not be located ({@link ExtensionException#INVALID_EXTENSION}).
932      */

933     public ExtensionBundle getExtensionBundle(String JavaDoc id) throws ExtensionException {
934         if (!extensionBundles.containsKey(id)) {
935             throw new ExtensionException(ExtensionException.INVALID_EXTENSION, id);
936         }
937         return (ExtensionBundle) extensionBundles.get(id);
938     }
939
940     /**
941      * Reload all
942      *
943      * @throws Exception
944      */

945     public void reload() throws Exception JavaDoc {
946         if (log.isInfoEnabled())
947             log.info("Reloading all application bundles");
948         boolean reconnect = downloadableExtensions != null;
949         downloadableExtensions = null;
950         downloadableExtensionsLastUpdated = null;
951         deregisterApplicationPermissions();
952
953         reloadAll();
954         if (reconnect) {
955             getDownloadableExtensionStoreDescriptor(true);
956         }
957     }
958
959     public static VersionInfo.Version getWorkingVersion() {
960         VersionInfo.Version version = new VersionInfo.Version(System.getProperty("sslexplorer.forceVersion", ContextHolder.getContext().getVersion().toString()));
961         return version;
962     }
963
964     /**
965      * @param id
966      * @throws ExtensionException
967      */

968     @SuppressWarnings JavaDoc("unchecked")
969     public void reload(String JavaDoc id) throws ExtensionException {
970         if (log.isInfoEnabled())
971             log.info("Reloading application bundle " + id);
972         if (isExtensionLoaded(id)) {
973             ExtensionBundle bundle = getExtensionBundle(id);
974             try {
975                 bundle.load();
976             } catch (ExtensionException ee) {
977                 log.warn("Failed to reload extension descriptor.", ee);
978                 extensionBundles.remove(id);
979                 extensionBundlesList.remove(bundle);
980                 throw ee;
981             }
982         } else {
983             loadDir(new File JavaDoc(basedir, id));
984         }
985         Collections.sort(extensionBundlesList);
986     }
987
988     /**
989      * @return File
990      * @throws IOException
991      */

992     public File JavaDoc getUpdatedExtensionsDirectory() throws IOException JavaDoc {
993         File JavaDoc updatedExtensionsDir = new File JavaDoc(ContextHolder.getContext().getConfDirectory(), "updated-extensions");
994         if (!updatedExtensionsDir.exists() && !updatedExtensionsDir.mkdirs()) {
995             throw new IOException JavaDoc("The extension update directory " + updatedExtensionsDir.getAbsolutePath()
996                 + " could not be created.");
997         }
998         return updatedExtensionsDir;
999     }
1000
1001    /**
1002     * Remove an extension bundle. The bundle will be stopped if it is started
1003     * and events will be fired. Global warnings will also be created if the
1004     * bundle contains any plugins informing the administrator the server must
1005     * be restarted.
1006     *
1007     * @param bundle bundle to remove
1008     * @throws Exception on any error
1009     */

1010    @SuppressWarnings JavaDoc("unchecked")
1011    public void removeExtensionBundle(ExtensionBundle bundle) throws Exception JavaDoc {
1012        if (log.isInfoEnabled())
1013            log.info("Removing extension bundle " + bundle.getId());
1014        boolean containsPlugin = bundle.isContainsPlugin();
1015        try {
1016            CoreServlet.getServlet()
1017                            .fireCoreEvent(new CoreEvent(this,
1018                                            CoreEventConstants.REMOVING_EXTENSION,
1019                                            bundle,
1020                                            null,
1021                                            CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
1022                                bundle.getId())
1023                                            .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName()));
1024            bundle.removeBundle();
1025            VERSION_PREFS.remove(bundle.getId());
1026            CoreServlet.getServlet()
1027                            .fireCoreEvent(new CoreEvent(this,
1028                                            CoreEventConstants.REMOVE_EXTENSION,
1029                                            bundle,
1030                                            null,
1031                                            CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
1032                                bundle.getId())
1033                                            .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName()));
1034            if (containsPlugin) {
1035                if (!ExtensionStore.getInstance().isRepositoryBacked()) {
1036                    if (log.isInfoEnabled())
1037                        log.info("Extension " + bundle.getId() + " contains plugins, deferring removal until restart.");
1038                    StringBuffer JavaDoc toRemove = new StringBuffer JavaDoc(STORE_PREF.get(DIRS_TO_REMOVE, ""));
1039                    if (toRemove.length() > 0) {
1040                        toRemove.append(",");
1041                    }
1042                    toRemove.append(bundle.getBaseDir());
1043                    STORE_PREF.put(DIRS_TO_REMOVE, toRemove.toString());
1044                }
1045            }
1046            ExtensionStoreStatusManager.removeExtension(bundle.getId());
1047        } catch (Exception JavaDoc e) {
1048            CoreServlet.getServlet()
1049                            .fireCoreEvent(new CoreEvent(this, CoreEventConstants.REMOVE_EXTENSION, null, null, e).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
1050                                bundle.getId())
1051                                            .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName()));
1052
1053            throw e;
1054        } finally {
1055            if (!containsPlugin) {
1056                extensionBundles.remove(bundle.getId());
1057                extensionBundlesList.remove(bundle);
1058                if (log.isInfoEnabled())
1059                    log.info("Extension Zip file " + bundle.getId() + ".zip" + " has been deleted.");
1060            }
1061            if (ExtensionStore.getInstance().isRepositoryBacked()) {
1062                RepositoryFactory.getRepository().getStore(ExtensionStore.ARCHIVE_STORE).removeEntry(bundle.getId() + ".zip");
1063            }
1064            Collections.sort(extensionBundlesList);
1065        }
1066    }
1067
1068    /**
1069     * Determine if an extension is installed given its extension bundle id.
1070     *
1071     * @param id extension bundle id
1072     * @return true if the extension has been loaded
1073     */

1074    public boolean isExtensionLoaded(String JavaDoc id) {
1075        for (Iterator JavaDoc i = extensionBundlesList.iterator(); i.hasNext();) {
1076            ExtensionBundle bundle = (ExtensionBundle) i.next();
1077            if (bundle.containsApplication(id)) {
1078                return true;
1079            }
1080        }
1081        return false;
1082    }
1083
1084    /**
1085     * Install an extension directly from the 3SP Extension Store given its id
1086     * and an input stream providing the extension bundle archive in zip format.
1087     * This method will also attempt to install all of the bundles dependencies.
1088     *
1089     * @param id extension bundle id
1090     * @param in input stream of extension bundle archive (Zip format)
1091     * @param request request object
1092     * @param contentLength size of extension bundle if known (-1 when unavailable - used for progress bar)
1093     * @return ExtensionBundle installed extension bundle
1094     * @throws ExtensionException on any error
1095     */

1096    public ExtensionBundle installExtensionFromStore(final String JavaDoc id, InputStream JavaDoc in, HttpServletRequest JavaDoc request, long contentLength)
1097                    throws ExtensionException {
1098        ExtensionStoreDescriptor store;
1099        try {
1100            // Get the application store descriptor
1101
store = getDownloadableExtensionStoreDescriptor(true);
1102            if (store == null) {
1103                throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "No downloadable applications.");
1104            }
1105
1106            ExtensionBundle bundle = store.getApplicationBundle(id);
1107            if (bundle == null) {
1108                throw new ExtensionException(ExtensionException.INVALID_EXTENSION, id);
1109            }
1110
1111            // Check host version
1112
Context context = ContextHolder.getContext();
1113            if (bundle.getRequiredHostVersion() != null && bundle.getRequiredHostVersion().compareTo(context.getVersion()) > 0) {
1114                throw new ExtensionException(ExtensionException.INSUFFICIENT_SSLEXPLORER_HOST_VERSION,
1115                                bundle.getId(),
1116                                bundle.getRequiredHostVersion().toString());
1117            }
1118
1119            // Install all dependencies
1120
if (bundle.getDependencies() != null) {
1121                for (String JavaDoc dep : bundle.getDependencies()) {
1122                    if (isExtensionBundleLoaded(dep)) {
1123                        ExtensionBundle current = getExtensionBundle(dep);
1124                        ExtensionBundle available = store.getApplicationBundle(dep);
1125                        if(available != null) {
1126                            if (!current.isDevExtension() && isNewerVersionAvailable(available, current)) {
1127                                if (log.isInfoEnabled())
1128                                    log.info("Found a dependency (" + dep + "), that needs upgrading. " + current.getVersion().toString() + " is the current version, " + available.getVersion().toString() + " is available. Installing now");
1129                                installExtensionFromStore(current.getId(), available.getVersion().toString(), request);
1130                            }
1131                        }
1132                    } else {
1133                        try {
1134                            if (log.isInfoEnabled())
1135                                log.info("Found a dependency (" + dep + "), that is not installed. Installing now");
1136                            installExtensionFromStore(dep, store.getApplicationBundle(dep).getVersion().toString(), request);
1137                        } catch (Exception JavaDoc e) {
1138                            throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "Failed to install dependency " + dep);
1139                        }
1140                    }
1141                }
1142            }
1143
1144            // This action may be wrapped in a task progress monitor
1145
Task task = (Task)request.getAttribute(TaskHttpServletRequest.ATTR_TASK);
1146            if(task != null && request.getAttribute(TaskHttpServletRequest.ATTR_TASK_PROGRESS_HANDLED_EXTERNALLY) == null) {
1147                TaskProgressBar bar = new TaskProgressBar("installExtension", 0, (int)contentLength, 0); // TODO should accept longs
1148
task.clearProgressBars();
1149                task.addProgressBar(bar);
1150                in = new TaskInputStream(bar, in);
1151                ((TaskInputStream)in).getProgressBar().setNote(new BundleActionMessage("extensions", "taskProgress.downloadExtension.note", id));
1152                if(!task.isConfigured())
1153                    task.configured();
1154            }
1155
1156            return installExtension(id, in);
1157        } catch (IOException JavaDoc jde) {
1158            throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "Failed to load descriptor.");
1159        } catch (JDOMException jde) {
1160            throw new ExtensionException(ExtensionException.FAILED_TO_PARSE_DESCRIPTOR);
1161        }
1162    }
1163
1164    /**
1165     * Install an extension directly from the 3SP Extension Store given its id.
1166     * This method will also attempt to install all of the bundles dependencies.
1167     *
1168     * @param id extension bundle ID
1169     * @param version
1170     * @param request request object
1171     * @throws ExtensionException extension errors
1172     * @throws IOException io errors
1173     */

1174    public void installExtensionFromStore(String JavaDoc id, String JavaDoc version, HttpServletRequest JavaDoc request) throws IOException JavaDoc, ExtensionException {
1175        URLConnection JavaDoc connection = downloadExtension(id, version);
1176        InputStream JavaDoc inputStream = connection.getInputStream();
1177        installExtensionFromStore(id, inputStream, request, connection.getContentLength());
1178    }
1179
1180    /**
1181     * Install an extension givens its bundle and an input stream providing the
1182     * extension bundle archive in zip format.
1183     *
1184     * @param id extension bundle ID
1185     * @param in input stream provided the extension bundle archive (in Zip
1186     * format)
1187     * @return ExtensionBundle
1188     * @throws IOException io errors
1189     * @throws ExtensionException extension errors
1190     */

1191    public ExtensionBundle installExtension(final String JavaDoc id, InputStream JavaDoc in) throws IOException JavaDoc, ExtensionException {
1192        streamToRepositoryStore(in, id);
1193
1194        try {
1195            RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
1196            ZipExtract.extractZipFile(getExtensionStoreDirectory(), repStore.getEntryInputStream(id + ".zip"));
1197            reload(id);
1198            ExtensionBundle newBundle = getExtensionBundle(id);
1199            installExtension(newBundle);
1200            fireBundleEvent(CoreEventConstants.INSTALL_EXTENSION, newBundle);
1201        } catch (IOException JavaDoc e) {
1202            CoreServlet.getServlet().fireCoreEvent(new CoreEvent(this,
1203                            CoreEventConstants.INSTALL_EXTENSION,
1204                            null,
1205                            null,
1206                            CoreEvent.STATE_UNSUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID, id));
1207            throw e;
1208        } catch (ExtensionException e) {
1209            CoreServlet.getServlet().fireCoreEvent(new CoreEvent(this,
1210                            CoreEventConstants.INSTALL_EXTENSION,
1211                            null,
1212                            null,
1213                            CoreEvent.STATE_UNSUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID, id));
1214            throw e;
1215        }
1216        return getExtensionBundle(id);
1217    }
1218
1219    /**
1220     * Check the license for a given bundle, adding a page redirect to ask the
1221     * user to accept a license agreement if required.
1222     * <p>
1223     * This should be called just after an extenion is installed either from the
1224     * 3SP Application Store or manually.
1225     *
1226     * @param newBundle newly installed bundle
1227     * @param request request object
1228     * @param installedForward the forward to redirect to after license
1229     * agreement has been show
1230     * @throws Exception on any error
1231     */

1232    public void licenseCheck(final ExtensionBundle newBundle, HttpServletRequest JavaDoc request, final ActionForward installedForward)
1233                    throws Exception JavaDoc {
1234
1235        final RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
1236        // If installing, there may be a license agreement to handle
1237
File JavaDoc licenseFile = newBundle.getLicenseFile();
1238        if (licenseFile != null && licenseFile.exists()) {
1239            LicenseAgreement licenseAgreement = getLicenseAgreement(newBundle, repStore, licenseFile, installedForward);
1240            CoreUtil.requestLicenseAgreement(request.getSession(), licenseAgreement);
1241        }
1242    }
1243
1244    /**
1245     * Invoked after an extension has been installed, this method adds a global
1246     * warning if the bundle contains any plugins indicating a restart is neeed.
1247     * Also, if the bundle doesn't contain any plugins it will immediately run
1248     * the 'installer'.
1249     *
1250     * @param newBundle newly installed extension bundle
1251     * @param request request
1252     * @throws Exception on any error
1253     */

1254    public void postInstallExtension(final ExtensionBundle newBundle, HttpServletRequest JavaDoc request) throws Exception JavaDoc {
1255        boolean containsPlugin = newBundle.isContainsPlugin();
1256
1257        if (containsPlugin) {
1258            CoreUtil.addMultipleGlobalWarning(GlobalWarning.MANAGEMENT_USERS, new BundleActionMessage("extensions",
1259                            "extensionStore.message.pluginInstalledRestartRequired"));
1260            newBundle.setType(ExtensionBundle.TYPE_PENDING_INSTALLATION);
1261        } else {
1262            newBundle.start();
1263
1264            // Installer must be run after start as it may have custom tasks
1265
if (newBundle.getInstaller() != null) {
1266                newBundle.getInstaller().doInstall(null);
1267            }
1268
1269            newBundle.activate();
1270        }
1271    }
1272
1273    /**
1274     * Update an extension givens its bundle and an input stream providing the
1275     * extension bundle archive in zip format.
1276     *
1277     * @param id extension bundle id
1278     * @param in input stream of extension bundle archive (Zip format)
1279     * @param request reuqest object
1280     * @param contentLength content length
1281     * @return ExtensionBundle newly loaded extension bundle
1282     * @throws Exception on any error
1283     */

1284    public ExtensionBundle updateExtension(String JavaDoc id, InputStream JavaDoc in, HttpServletRequest JavaDoc request, long contentLength) throws Exception JavaDoc {
1285        ExtensionStoreDescriptor store;
1286        // Get the application store descriptor
1287
/**
1288         * LDP - Why does this require the extension store descriptor? Extensions should be
1289         * updatable even if the extension store is not available!!!!!!!!
1290         */

1291// store = getDownloadableExtensionStoreDescriptor(true);
1292
// if (store == null) {
1293
// throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "No downloadable applications.");
1294
// }
1295

1296
1297        // This action may be wrapped in a task progress monitor
1298
Task task = (Task)request.getAttribute(TaskHttpServletRequest.ATTR_TASK);
1299        if(task != null && request.getAttribute(TaskHttpServletRequest.ATTR_TASK_PROGRESS_HANDLED_EXTERNALLY) == null) {
1300            TaskProgressBar bar = new TaskProgressBar("updateExtension", 0, (int)contentLength, 0);
1301            task.addProgressBar(bar);
1302            in = new TaskInputStream(bar, in);
1303            ((TaskInputStream)in).getProgressBar().setNote(new BundleActionMessage("extensions", "taskProgress.downloadExtension.note", id));
1304            task.configured();
1305        }
1306
1307        ExtensionBundle currentBundle = getExtensionBundle(id);
1308        if (currentBundle == null) {
1309            throw new ExtensionException(ExtensionException.INVALID_EXTENSION, id);
1310        }
1311
1312        try {
1313            return updateExtension(currentBundle, in, request);
1314        } catch (ExtensionException ee) {
1315            CoreServlet.getServlet()
1316                            .fireCoreEvent(new CoreEvent(this,
1317                                            CoreEventConstants.UPDATE_EXTENSION,
1318                                            null,
1319                                            null,
1320                                            CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
1321                                                currentBundle.getId())
1322                                            .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, currentBundle.getName())
1323                                            .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_VERSION,
1324                                                currentBundle.getVersion().toString()));
1325            throw ee;
1326        }
1327    }
1328
1329    private ExtensionBundle updateExtension(ExtensionBundle currentBundle, InputStream JavaDoc in,
1330                                            HttpServletRequest JavaDoc request) throws Exception JavaDoc {
1331
1332        // Check host version
1333
Context context = ContextHolder.getContext();
1334        if (currentBundle.getRequiredHostVersion() != null && currentBundle.getRequiredHostVersion().compareTo(context.getVersion()) > 0) {
1335            throw new ExtensionException(ExtensionException.INSUFFICIENT_SSLEXPLORER_HOST_VERSION,
1336                currentBundle.getId(),
1337                currentBundle.getRequiredHostVersion().toString());
1338        }
1339
1340        boolean containsPlugin = currentBundle.isContainsPlugin();
1341
1342        // Remove Extension Bundle;
1343
try {
1344            currentBundle.removeBundle();
1345            VERSION_PREFS.remove(currentBundle.getId());
1346        } catch (Exception JavaDoc e) {
1347            throw e;
1348        } finally {
1349            if (!containsPlugin) {
1350                extensionBundles.remove(currentBundle.getId());
1351                extensionBundlesList.remove(currentBundle);
1352                if (log.isInfoEnabled())
1353                    log.info("Extension Zip file " + currentBundle.getId() + ".zip" + " has been deleted.");
1354            }
1355        }
1356
1357        // Install Extension;
1358
streamToRepositoryStore(in, currentBundle.getId());
1359        RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
1360        ZipExtract.extractZipFile(getExtensionStoreDirectory(), repStore.getEntryInputStream(currentBundle.getId() + ".zip"));
1361
1362        if (containsPlugin) {
1363            currentBundle.setType(ExtensionBundle.TYPE_PENDING_UPDATE);
1364            fireBundleEvent(CoreEventConstants.UPDATE_EXTENSION, currentBundle);
1365            return currentBundle;
1366        } else {
1367            reload(currentBundle.getId());
1368            ExtensionBundle newBundle = getExtensionBundle(currentBundle.getId());
1369            installExtension(newBundle);
1370            postInstallExtension(newBundle, request);
1371            if (newBundle.getStatus().isStartedOrActivated()) {
1372                newBundle.stop();
1373                newBundle.start();
1374                newBundle.activate();
1375            }
1376            fireBundleEvent(CoreEventConstants.UPDATE_EXTENSION, newBundle);
1377            return newBundle;
1378        }
1379    }
1380
1381    /**
1382     * Set the <i>Edition</i>, i.e. GPL, Community or Enterprise.
1383     *
1384     * @param currentEdition edition
1385     */

1386    public static void setCurrentEdition(String JavaDoc currentEdition) {
1387        ExtensionStore.currentEdition = currentEdition;
1388    }
1389
1390    private LicenseAgreement getLicenseAgreement(final ExtensionBundle newBundle, final RepositoryStore repStore,
1391                                                    final File JavaDoc licenseFile, final ActionForward installedForward) {
1392        return new LicenseAgreement(newBundle.getName(), licenseFile, new LicenseAgreementCallback() {
1393            public void licenseAccepted(HttpServletRequest JavaDoc request) {
1394                // Dont care
1395
}
1396
1397            public void licenseRejected(HttpServletRequest JavaDoc request) {
1398                // Remove the repository entry if it is in
1399
// use
1400
if (isRepositoryBacked()) {
1401                    try {
1402                        repStore.removeEntry(newBundle.getId() + ".zip");
1403                    } catch (IOException JavaDoc ex) {
1404                    }
1405                }
1406
1407                // Remove the expanded bundle
1408
if (newBundle.getBaseDir().exists()) {
1409                    Util.delTree(newBundle.getBaseDir());
1410                }
1411
1412                // Reload the extension store
1413
try {
1414                    reload(newBundle.getId());
1415                } catch (Exception JavaDoc e) {
1416                    log.error("Failed to reload extension store.");
1417                }
1418            }
1419        }, installedForward);
1420    }
1421
1422    private static boolean isNewerVersionAvailable(ExtensionBundle available, ExtensionBundle current) {
1423        VersionInfo.Version v1 = new VersionInfo.Version(available.getVersion().toString());
1424        VersionInfo.Version v2 = new VersionInfo.Version(current.getVersion().toString());
1425        return v1.compareTo(v2) > 0;
1426    }
1427
1428    private static void streamToRepositoryStore(InputStream JavaDoc in, String JavaDoc bundleId) throws IOException JavaDoc {
1429        RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
1430        OutputStream JavaDoc out = null;
1431        try {
1432            out = repStore.getEntryOutputStream(bundleId + ".zip");
1433            Util.copy(in, out);
1434        } finally {
1435            Util.closeStream(in);
1436            Util.closeStream(out);
1437        }
1438    }
1439
1440    private void fireBundleEvent(int eventType, ExtensionBundle bundle) {
1441        String JavaDoc extensionType = getExtensionType(bundle);
1442        CoreServlet.getServlet()
1443                        .fireCoreEvent(new CoreEvent(this, eventType, null, null, CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
1444                            bundle.getId())
1445                                        .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName())
1446                                        .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_VERSION,
1447                                            bundle.getVersion().toString())
1448                                        .addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_TYPE, extensionType));
1449    }
1450
1451    private static String JavaDoc getExtensionType(ExtensionBundle bundle) {
1452        for (Iterator JavaDoc itr = bundle.iterator(); itr.hasNext();) {
1453            ExtensionDescriptor descriptor = (ExtensionDescriptor) itr.next();
1454            if (descriptor.getExtensionType() instanceof ExtensionType) {
1455                return descriptor.getExtensionType().getType();
1456            }
1457        }
1458        return null;
1459    }
1460
1461    private void addAdditionalWebResource() {
1462        //
1463
String JavaDoc additionalWebResources = System.getProperty("sslexplorer.additionalWebResourceDirectories", "");
1464        if (additionalWebResources != null) {
1465            StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(additionalWebResources, ",");
1466            while (t.hasMoreTokens()) {
1467                try {
1468                    URL JavaDoc u = null;
1469                    String JavaDoc dir = t.nextToken();
1470                    if (dir.endsWith("]")) {
1471                        int idx = dir.indexOf('[');
1472                        if (idx != -1) {
1473                            dir = dir.substring(0, idx);
1474                            u = new File JavaDoc(dir).getCanonicalFile().toURL();
1475                            log.warn("Associating additional web resource directories with plugins is no longer supported.");
1476                        }
1477                    }
1478                    if (u == null) {
1479                        u = new File JavaDoc(dir).getCanonicalFile().toURL();
1480                    }
1481                    ContextHolder.getContext().addResourceBase(u);
1482                } catch (Exception JavaDoc e) {
1483                    log.error("Failed to add additional web resources directory.", e);
1484                }
1485            }
1486        }
1487    }
1488
1489    private void addAdditionalClasspath() {
1490
1491        // Add any additional class path elements
1492
StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(System.getProperty("sslexplorer.additionalClasspath", ""), ",");
1493        while (t.hasMoreTokens()) {
1494            try {
1495                String JavaDoc sf = t.nextToken();
1496                File JavaDoc[] f = null;
1497                if (sf.endsWith("/*.jar")) {
1498                    f = new File JavaDoc(sf.substring(0, sf.length() - 6)).listFiles(new FileFilter JavaDoc() {
1499                        public boolean accept(File JavaDoc pathname) {
1500                            return pathname.getName().toLowerCase().endsWith(".jar");
1501                        }
1502                    });
1503                } else {
1504                    f = new File JavaDoc[1];
1505                    f[0] = new File JavaDoc(sf);
1506                }
1507                for (int j = 0; f != null && j < f.length; j++) {
1508                    if (f[j].exists() && (f[j].isDirectory() || f[j].getName().toLowerCase().endsWith(".jar"))) {
1509                        URL JavaDoc u = f[j].toURL();
1510                        ContextHolder.getContext().addContextLoaderURL(u);
1511                    }
1512                }
1513            } catch (MalformedURLException JavaDoc murle) {
1514                log.warn("Invalid element in additional classpaths");
1515            }
1516
1517        }
1518    }
1519
1520    private void initialiseRepository() {
1521        RepositoryStore store = RepositoryFactory.getRepository().getStore("archives");
1522        // Remove the existing extensions
1523
if (basedir.exists()) {
1524            Util.delTree(basedir);
1525        }
1526
1527        // Now recreate all extensions from the repository
1528
basedir.mkdirs();
1529        String JavaDoc[] archives = store.listEntries();
1530        for (int i = 0; i < archives.length; i++) {
1531            if (log.isInfoEnabled()) {
1532                log.info("Extracting archive " + archives[i]);
1533            }
1534            try {
1535                ZipExtract.extractZipFile(basedir, store.getEntryInputStream(archives[i]));
1536                if (log.isInfoEnabled()) {
1537                    log.info("Completed archive extraction for extension " + archives[i]);
1538                }
1539            } catch (IOException JavaDoc ex) {
1540                log.error("Error extracting archive for extension " + archives[i], ex);
1541                Util.delTree(new File JavaDoc(basedir, archives[i]));
1542            }
1543        }
1544    }
1545
1546    private void loadDevExtensions() {
1547        List JavaDoc<String JavaDoc> devExtensions = new ArrayList JavaDoc<String JavaDoc>();
1548        String JavaDoc extensionList = System.getProperty("sslexplorer.devExtensions", "");
1549        StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(extensionList, ",");
1550        while (t.hasMoreTokens()) {
1551            String JavaDoc ext = t.nextToken();
1552            if (ext.equalsIgnoreCase("all")) {
1553                File JavaDoc f = new File JavaDoc(System.getProperty("user.dir")).getParentFile();
1554                File JavaDoc[] dirs = f.listFiles(new FileFilter JavaDoc() {
1555                    public boolean accept(File JavaDoc pathname) {
1556                        File JavaDoc f = new File JavaDoc(pathname, "extensions");
1557                        return f.exists() && f.isDirectory();
1558                    }
1559                });
1560                for (int i = 0; dirs != null && i < dirs.length; i++) {
1561                    devExtensions.add(dirs[i].getName());
1562                }
1563            } else if (ext.equalsIgnoreCase("enterprise")) {
1564                File JavaDoc f = new File JavaDoc(System.getProperty("user.dir")).getParentFile();
1565                File JavaDoc[] dirs = f.listFiles(new FileFilter JavaDoc() {
1566                    public boolean accept(File JavaDoc pathname) {
1567                        File JavaDoc f = new File JavaDoc(pathname, "extensions");
1568                        return f.exists() && f.isDirectory() && pathname.getName().indexOf("sslexplorer-enterprise-") != -1;
1569                    }
1570                });
1571                for (int i = 0; dirs != null && i < dirs.length; i++) {
1572                    devExtensions.add(dirs[i].getName());
1573                }
1574            } else if (ext.equalsIgnoreCase("community")) {
1575                File JavaDoc f = new File JavaDoc(System.getProperty("user.dir")).getParentFile();
1576                File JavaDoc[] dirs = f.listFiles(new FileFilter JavaDoc() {
1577                    public boolean accept(File JavaDoc pathname) {
1578                        File JavaDoc f = new File JavaDoc(pathname, "extensions");
1579                        return f.exists() && f.isDirectory() && pathname.getName().indexOf("sslexplorer-community-") != -1;
1580                    }
1581                });
1582                for (int i = 0; dirs != null && i < dirs.length; i++) {
1583                    devExtensions.add(dirs[i].getName());
1584                }
1585            } else {
1586                if (ext.startsWith("!")) {
1587                    devExtensions.remove(ext.substring(1));
1588                } else {
1589                    devExtensions.add(ext);
1590                }
1591            }
1592        }
1593
1594        for (Iterator JavaDoc it = devExtensions.iterator(); it.hasNext();) {
1595            String JavaDoc ext = (String JavaDoc) it.next();
1596            File JavaDoc d = new File JavaDoc(new File JavaDoc(System.getProperty("user.dir")).getParentFile(), ext);
1597            File JavaDoc extensionDir = new File JavaDoc(new File JavaDoc(d, "extensions"), d.getName());
1598            File JavaDoc extensionDescriptor = new File JavaDoc(extensionDir, "extension.xml");
1599            if (extensionDescriptor.exists()) {
1600                try {
1601                    loadBundle(extensionDescriptor, true);
1602                } catch (Exception JavaDoc e) {
1603                    log.error("Failed to load dev extension " + extensionDescriptor.getAbsolutePath(), e);
1604                }
1605            }
1606        }
1607    }
1608
1609    class BundleComparator implements Comparator JavaDoc<ExtensionBundle> {
1610        public int compare(ExtensionBundle o1, ExtensionBundle o2) {
1611            int i = new Integer JavaDoc(o1.getOrder()).compareTo(new Integer JavaDoc(o2.getOrder()));
1612            return i == 0 ? o1.getId().compareTo(o2.getId()) : i;
1613        }
1614    }
1615
1616}
Popular Tags