/*
* Adito
*
* Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.adito.extensions.store;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionForward;
import org.jdom.JDOMException;
import com.adito.boot.Context;
import com.adito.boot.ContextHolder;
import com.adito.boot.PropertyList;
import com.adito.boot.RepositoryFactory;
import com.adito.boot.RepositoryStore;
import com.adito.boot.SystemProperties;
import com.adito.boot.Util;
import com.adito.boot.VersionInfo;
import com.adito.core.BundleActionMessage;
import com.adito.core.CoreAttributeConstants;
import com.adito.core.CoreEvent;
import com.adito.core.CoreEventConstants;
import com.adito.core.CoreMessageResources;
import com.adito.core.CoreServlet;
import com.adito.core.CoreUtil;
import com.adito.core.GlobalWarning;
import com.adito.core.GlobalWarningManager;
import com.adito.core.LicenseAgreement;
import com.adito.core.GlobalWarning.DismissType;
import com.adito.extensions.ExtensionBundle;
import com.adito.extensions.ExtensionDescriptor;
import com.adito.extensions.ExtensionException;
import com.adito.extensions.ExtensionInstaller;
import com.adito.extensions.ExtensionType;
import com.adito.extensions.ExtensionBundle.ExtensionBundleStatus;
import com.adito.setup.LicenseAgreementCallback;
import com.adito.tasks.Task;
import com.adito.tasks.TaskHttpServletRequest;
import com.adito.tasks.TaskInputStream;
import com.adito.tasks.TaskProgressBar;
import com.adito.util.ZipExtract;
/**
* Manages all aspects of <i>Extensions</i> including loading, starting,
* activating, disabling, installing, remove and updating.
* <p>
* This class also manages the interaction with the <i>Adito Extension Store</i>,
* including update checking and installing from the store.
*/
public class ExtensionStore {
public static final String HTTP_3SP_COM_APPSTORE = "http://www.aditoproject.org/appstore/";
private static final Log log = LogFactory.getLog(ExtensionStore.class);
private static final String DIRS_TO_REMOVE = "dirsToRemove";
private static String currentEdition = "Community Edition";
public static final String UPDATEABLE = "Updateable";
/**
* Name of store in repository for extension bundle archives
*/
public static final String ARCHIVE_STORE = "archives";
/**
* 'Installed' category in extension manager
*/
public static final String INSTALLED_CATEGORY = "Installed";
/**
* The extension bundle id for the Agent.
*/
public static final String AGENT_EXTENSION_BUNDLE_ID = "adito-agent";
/**
* Preferences node for storing current extension version and other
* extension related details
*/
public static final Preferences PREFS = ContextHolder.getContext().getPreferences().node("extensions");
/**
* Preferences node with the extensions node for storing extension store
* related details.
*/
public static final Preferences STORE_PREF = PREFS.node("store");
/**
* Preferences node with the extensions node for storing extension version
* details.
*/
public static final Preferences VERSION_PREFS = PREFS.node("versions");
/**
* Extension store connect timeout
*/
public static final int CONNECT_TIMEOUT = 30000;
/**
* Extension store read timeout
*/
public static final int READ_TIMEOUT = 30000;
// Private instance variables
private File basedir;
private Map<String, ExtensionBundle> extensionBundles;
private List<ExtensionBundle> extensionBundlesList;
private List<ExtensionInstaller> extensionBundleInstallList;
private ExtensionStoreDescriptor downloadableExtensions;
private Calendar downloadableExtensionsLastUpdated;
private boolean repositoryBacked;
private static ExtensionStore instance;
//private UpdateChecker updateChecker;
/**
* Get an instance of the extension store.
*
* @return instance
*/
public static ExtensionStore getInstance() {
if (instance == null) {
instance = new ExtensionStore();
}
return instance;
}
/**
* Get the update checker that checks for update to the core, extensions and
* loads the RSS feeds
*
* @return update checker
*/
//public UpdateChecker getUpdateChecker() {
// return updateChecker;
//}
/**
* Get the directory where expanded extensions are stored.
*
* @return the extension store directory
*/
public File getExtensionStoreDirectory() {
return basedir;
}
/**
* Get if the extension store is 'Repository Backed'. See class description
* for details.
*
* @return true if it is repository backed
*/
public boolean isRepositoryBacked() {
return repositoryBacked;
}
/**
* Initialise the extension store.
*
* @param basedir
* @throws IOException
*/
public void init(File basedir) throws IOException {
//updateChecker = new UpdateChecker();
// Get if the application store comes from the repository
repositoryBacked = "true".equals(SystemProperties.get("adito.extensions.repositoryBacked", "true"));
this.basedir = basedir;
extensionBundles = new HashMap<String, ExtensionBundle>();
extensionBundlesList = new ArrayList<ExtensionBundle>();
extensionBundleInstallList = new ArrayList<ExtensionInstaller>();
if (isRepositoryBacked()) {
initialiseRepository();
}
try {
loadAll();
// TODO display errors to use somehow
} catch (Exception e) {
log.error("Failed extract extension bundles from repository.", e);
}
/*
* A lot of plugins were made incompatible at 0.2.10. Here we make sure
* any incompatible extensions are disabled
*/
VersionInfo.Version sslxVersion = ContextHolder.getContext().getVersion();
if (sslxVersion.getMajor() == 0 && sslxVersion.getMinor() == 2 && sslxVersion.getBuild() == 10) {
StringBuffer buf = new StringBuffer();
for (ExtensionBundle bundle : extensionBundlesList) {
if (bundle.getRequiredHostVersion() == null || bundle.getRequiredHostVersion().compareTo(sslxVersion) < 0) {
log.warn("Extension " + bundle.getId()
+ " has a required host version of "
+ bundle.getRequiredHostVersion()
+ " where as "
+ "this version is "
+ sslxVersion
+ ". This plugin will be disabled.");
ExtensionStoreStatusManager.systemDisableExtension(bundle.getId());
if (buf.length() > 0) {
buf.append(",");
}
buf.append(bundle.getName());
}
}
if (buf.length() > 0) {
GlobalWarningManager.getInstance().addMultipleGlobalWarning(
new GlobalWarning(GlobalWarning.MANAGEMENT_USERS, new BundleActionMessage("extensions",
"startup.disabledExtensions", buf.toString()), DismissType.DISMISS_FOR_USER));
}
}
/*
* First remove any plugins whose uninstallation may have been deferred
* until restart
*/
if (!isRepositoryBacked()) {
String dirsToRemove = STORE_PREF.get(DIRS_TO_REMOVE, "");
if (!dirsToRemove.equals("")) {
StringTokenizer t = new StringTokenizer(dirsToRemove, ",");
while (t.hasMoreTokens()) {
File dir = new File(t.nextToken());
if (dir.exists()) {
if (log.isInfoEnabled())
log.info("Removing extension " + dir.getAbsolutePath());
Util.delTree(dir);
}
}
STORE_PREF.remove(DIRS_TO_REMOVE);
}
/*
* Check for extension updates
*/
File updatedExtensionsDir = getUpdatedExtensionsDirectory();
File[] extensions = updatedExtensionsDir.listFiles();
if (extensions != null) {
for (int i = 0; i < extensions.length; i++) {
File destDir = new File(ContextHolder.getContext().getApplicationDirectory(), extensions[i].getName());
if (destDir.exists()) {
if (log.isInfoEnabled())
log.info("Removing extension " + destDir.getAbsolutePath());
if (!Util.delTree(destDir)) {
throw new IOException("Failed to remove old extension " + destDir.getAbsolutePath());
}
}
if (log.isInfoEnabled())
log.info("Moving " + extensions[i].getAbsolutePath() + " to " + destDir.getAbsolutePath());
if (!extensions[i].renameTo(destDir)) {
throw new IOException("Failed to rename extension " + extensions[i].getAbsolutePath()
+ " to "
+ destDir.getAbsolutePath());
}
}
}
}
// Add any additional class path elements
addAdditionalClasspath();
addAdditionalWebResource();
// Check for any mandatory updates
//try {
// updateChecker.initialise();
//} catch (Exception e) {
/*
* There is no need to prevent start up if we fail to get the
* available versions.
*/
// log.error("Failed to check for any extension updates.", e);
//}
}
/**
* @return the available extension bundles
*/
@SuppressWarnings("unchecked")
public List<ExtensionBundle> getAllAvailableExtensionBundles() {
List<ExtensionBundle> all = new ArrayList<ExtensionBundle>(extensionBundlesList);
try {
ExtensionStoreDescriptor descriptor = getDownloadableExtensionStoreDescriptor(downloadableExtensions != null, getWorkingVersion());
if (descriptor != null && descriptor.getExtensionBundles() != null) {
for (Iterator itr = descriptor.getExtensionBundles().iterator(); itr.hasNext();) {
ExtensionBundle bundle = (ExtensionBundle) itr.next();
// If the app is already installed, remove dont include it
// in the list
if (!extensionBundles.containsKey(bundle.getId())) {
all.add(bundle);
}
}
}
} catch (Exception e) {
log.error("Failed to get downloadable extensions.", e);
}
Collections.sort(all);
return all;
}
/**
* Get all the available extension bundles fro a given category.
*
* @param category
* @return the available extension bundles
*/
@SuppressWarnings("unchecked")
public List<ExtensionBundle> getAllAvailableExtensionBundles(String category) {
// just get the installed ones.
if (category.equals(INSTALLED_CATEGORY)){
return extensionBundlesList;
}
List<ExtensionBundle> all = new ArrayList<ExtensionBundle>();
if (category.equals(UPDATEABLE)){
for (ExtensionBundle bundle : extensionBundles.values()) {
// add all the updateable extensions
if (bundle.isUpdateable()){
all.add(bundle);
}
}
}
try {
ExtensionStoreDescriptor descriptor = getDownloadableExtensionStoreDescriptor(downloadableExtensions != null, getWorkingVersion());
if (descriptor != null && descriptor.getExtensionBundles() != null) {
for (Iterator itr = descriptor.getExtensionBundles().iterator(); itr.hasNext();) {
ExtensionBundle bundle = (ExtensionBundle) itr.next();
// If the app is already installed, remove dont include it
// in the list
if (!extensionBundles.containsKey(bundle.getId()) && bundle.getCategory().equals(category)) {
all.add(bundle);
}
}
}
} catch (Exception e) {
log.error("Failed to get downloadable extensions.", e);
}
Collections.sort(all);
return all;
}
/**
* @param id
* @param version
* @return URLConnection
* @throws IOException
*/
public URLConnection downloadExtension(String id, String version) throws IOException {
URL downloadURL = getDownloadURL(id, version);
if (downloadURL != null) {
if (log.isInfoEnabled())
log.info("Downloading extension from " + downloadURL.toExternalForm());
URLConnection con = downloadURL.openConnection();
con.setConnectTimeout(CONNECT_TIMEOUT);
con.setReadTimeout(READ_TIMEOUT);
con.connect();
return con;
} else {
throw new IOException("No valid download location for " + id);
}
}
/**
* Start all extensions bundles
*
* @throws ExtensionException any error starting a bundle. If multiple
* extensions are started then only the first exception thrown by
* the bundle will be thrown from this method, an attempt will be
* made to start all other extensions bundles.
*/
public void start() throws ExtensionException {
if (log.isInfoEnabled())
log.info("Starting extension store. Extensions will start in the following order .. ");
Collections.sort(extensionBundlesList, new BundleComparator());
for (ExtensionBundle bundle : extensionBundlesList) {
log.info(" " + bundle.getId() + " (" + bundle.getOrder() + ")");
}
for (ExtensionBundle bundle : extensionBundlesList) {
boolean setupMode = ContextHolder.getContext().isSetupMode();
if (!setupMode || (setupMode && bundle.isStartOnSetupMode())) {
ContextHolder.getContext().getBootProgressMonitor().updateMessage("Starting " + bundle.getName());
ContextHolder.getContext()
.getBootProgressMonitor()
.updateProgress((int) (30 + (10 * ((float) extensionBundlesList.indexOf(bundle) / extensionBundlesList.size()))));
// Start the bundle
try {
if (bundle.getStatus() == ExtensionBundleStatus.ENABLED) {
bundle.start();
}
} catch (Throwable t) {
/*
* Catch throwable to prevent bad extensions from interferring
* with the core (e.g. NoClassDefFoundError,
* ClassNotFoundException)
*/
log.error("Failed to start extension bundle.", t);
}
}
}
/*
* First check which extensions should have their installers run
*/
checkExtensionsForInstallation();
/*
* Now run the installer for the start phase
*/
performInstalls(ExtensionInstaller.ON_START);
}
/**
* Stop all extensions bundles
*
* @throws ExtensionException any error stopping a bundle. If multiple
* extensions are started then only the first exception thrown by
* the bundle will be thrown from this method, an attempt will be
* made to start all other extensions bundles.
*/
public void stop() throws ExtensionException {
if (log.isInfoEnabled())
log.info("Stopping extensions");
// ensure all threads are finished.
//updateChecker.end();
// Stop extensions in the reverse order they were started
if(extensionBundlesList != null) {
Collections.reverse(extensionBundlesList);
ExtensionException ee = null;
for (ExtensionBundle bundle : extensionBundlesList) {
try {
bundle.stop();
} catch (ExtensionException e) {
if (ee == null) {
ee = e;
}
log.error("Failed to stop extension bundle.", ee);
}
}
if (ee != null) {
throw ee;
}
}
}
/**
* Activate all extensions bundles
*
* @throws ExtensionException any error activating a bundle. If multiple
* extensions are started then only the first exception thrown by
* the bundle will be thrown from this method, an attempt will be
* made to start all other extensions bundles.
*/
public void activate() throws ExtensionException {
if (log.isInfoEnabled())
log.info("Activating extension store.");
StringBuffer buf = new StringBuffer();
for (ExtensionBundle bundle : extensionBundlesList) {
try {
if (bundle.getStatus() == ExtensionBundleStatus.STARTED) {
ContextHolder.getContext().getBootProgressMonitor().updateMessage("Activating " + bundle.getName());
ContextHolder.getContext()
.getBootProgressMonitor()
.updateProgress((int) (65 + (10 * ((float) extensionBundlesList.indexOf(bundle) / extensionBundlesList.size()))));
bundle.activate();
if(buf.length() != 0)
buf.append(",");
buf.append(bundle.getId());
}
} catch (Throwable t) {
/*
* Catch throwable to prevent bad extensions from interferring
* with the core (e.g. NoClassDefFoundError,
* ClassNotFoundException)
*/
log.error("Failed to activate extension bundle.", t);
}
}
/*
* Remove the versions of any extensions that no longer exist
*/
String[] k;
try {
k = VERSION_PREFS.keys();
for (int i = 0; i < k.length; i++) {
if (!isExtensionBundleLoaded(k[i])) {
VERSION_PREFS.remove(k[i]);
}
}
} catch (BackingStoreException e) {
log.warn("Could not clean up extension versions preferences node.", e);
}
// Start watching for version updates and RSS updates (if enabled)
//updateChecker.start();
/*
* Now run the installer for the start phase
*/
performInstalls(ExtensionInstaller.ON_ACTIVATE);
/**
* If activat plugins has changed since the last full activation
* then clear out the compiled JSP files
*/
if(!buf.toString().equals(PREFS.get("lastActivatedPlugins", ""))) {
Util.delTree(new File(ContextHolder.getContext().getTempDirectory(), "org"));
}
PREFS.put("lastActivatedPlugins", buf.toString());
/* Flush these preferences now incase the server is terminated before
* it gets a chance to write the preferences. When running in a development,
* its possible for the server to get confused about when it should
* clear out the temporary temporary directory.
*/
try {
PREFS.flush();
}
catch(BackingStoreException bse) {
}
}
/**
* reset
*/
public void resetExtensionStoreUpdate() {
downloadableExtensions = null;
}
/**
* @param connect
* @return ExtensionStoreDescriptor
* @throws IOException
* @throws JDOMException
*/
public ExtensionStoreDescriptor getDownloadableExtensionStoreDescriptor(boolean connect) throws IOException, JDOMException {
return getDownloadableExtensionStoreDescriptor(connect, getWorkingVersion());
}
/**
* @param connect
* @param version
* @return ExtensionStoreDescriptor
* @throws IOException
* @throws JDOMException
*/
public ExtensionStoreDescriptor getDownloadableExtensionStoreDescriptor(boolean connect, VersionInfo.Version version) throws IOException, JDOMException {
if (downloadableExtensions != null && downloadableExtensionsLastUpdated != null) {
Calendar calendar = ((Calendar) downloadableExtensionsLastUpdated.clone());
calendar.add(Calendar.DAY_OF_MONTH, 1);
if (new GregorianCalendar().after(calendar)) {
if (log.isInfoEnabled())
log.info("Downloadable extensions are out of date, will contact the update site again.");
downloadableExtensions = null;
}
}
if (downloadableExtensions == null && connect) {
URL storeURL = getStoreDownloadURL(ExtensionStore.HTTP_3SP_COM_APPSTORE, version);
if (storeURL != null) {
if (log.isInfoEnabled())
log.info("Loading extension store descriptor from " + storeURL.getHost());
downloadableExtensions = new ExtensionStoreDescriptor(storeURL);
downloadableExtensionsLastUpdated = new GregorianCalendar();
for (ExtensionBundle extensionBundle : downloadableExtensions.getExtensionBundles()) {
try {
ExtensionBundle installedApp = getExtensionBundle(extensionBundle.getId());
if (!installedApp.isDevExtension() && installedApp.getVersion().compareTo(extensionBundle.getVersion()) < 0) {
if (log.isInfoEnabled())
log.info("Update found for extenions " + extensionBundle.getId());
installedApp.setType(ExtensionBundle.TYPE_UPDATEABLE);
installedApp.setUpdateVersion(extensionBundle.getVersion());
installedApp.setChanges(extensionBundle.getChanges());
}
} catch (Exception e) {
if (log.isInfoEnabled())
log.info("Extension " + extensionBundle.getId() + " is not installed.");
}
}
if (log.isInfoEnabled())
log.info("Extension store descriptor loaded from " + storeURL.getHost());
}
}
return downloadableExtensions;
}
void performInstalls(String phase) {
for (ExtensionInstaller installer : extensionBundleInstallList) {
try {
if (log.isInfoEnabled()) {
log.info("Performing installer for " + installer.getBundle().getName() + " phase " + phase);
}
installer.doInstall(phase);
} catch (Exception e) {
log.warn("Installer for " + installer.getBundle().getName() + " phase " + phase + " failed.", e);
}
}
}
void checkExtensionsForInstallation() {
extensionBundleInstallList.clear();
PropertyList forceInstalls = new PropertyList(SystemProperties.get("adito.forceInstallers", ""));
for (ExtensionBundle bundle : extensionBundlesList) {
if (bundle.getInstaller().getOpCount() > 0 && bundle.getStatus().isStartedOrActivated()) {
String ver = VERSION_PREFS.get(bundle.getId(), "");
boolean force = forceInstalls.contains(bundle.getId());
if (force || ver.equals("") || !ver.equals(bundle.getVersion().toString())) {
if(force) {
log.info("Will run installer for " + bundle.getId() + " because it has been forced by the adito.foreceInstallers property.");
}
else if (ver.equals("")) {
log.info("Will run installer for " + bundle.getId() + " because this is its first install");
} else {
log.info("Will run installer for " + bundle.getId()
+ " because the last installed version "
+ ver
+ " has been upgraded to "
+ bundle.getVersion().toString());
}
extensionBundleInstallList.add(bundle.getInstaller());
}
}
}
}
private static URL getDownloadURL(String id, String version) {
try {
String location = SystemProperties.get("adito.downloadableApplicationStore.location", ExtensionStore.HTTP_3SP_COM_APPSTORE);
location += Util.urlEncode(id) + "/" + Util.urlEncode(version) + "/" + Util.urlEncode(id) + ".zip";
return new URL(location);
} catch (MalformedURLException murle) {
try {
String path = SystemProperties.get("adito.downloadableApplications.location");
path = path.replaceAll("\\$\\{id\\}", id);
path = path.replaceAll("\\$\\{version\\}", version);
return new File(path).toURL();
} catch (MalformedURLException e) {
log.error("Invalid downloadable extension location specified in system property adito.downloadableApplicationStore.location, '" + SystemProperties.get("adito.downloadableApplicationStore.location")
+ "'. Must be either a URL or the file path of the store descriptor file.");
}
}
return null;
}
public static URL getStoreDownloadURL(String appStoreLocation, VersionInfo.Version version) {
try {
String location = SystemProperties.get("adito.downloadableApplicationStore.location", appStoreLocation);
location += "core/" + Util.urlEncode(version.toString()) + "/store.xml";
return new URL(location);
} catch (MalformedURLException murle) {
try {
return new File(SystemProperties.get("adito.downloadableApplicationStore.location")).toURL();
} catch (MalformedURLException e) {
log.error("Invalid downloadable extension store location specified in system property adito.downloadableApplicationStore.location, '" + SystemProperties.get("adito.downloadableApplicationStore.location")
+ "'. Must be either a URL or the file path of the store descriptor file.");
}
}
return null;
}
/**
*
*/
public void deregisterApplicationPermissions() {
Util.toDo("Deregister application permissions");
}
private synchronized void reloadAll() throws Exception {
CoreMessageResources resources = CoreServlet.getServlet().getExtensionStoreResources();
for (ExtensionBundle extensionBundle : extensionBundles.values()) {
for (ExtensionDescriptor descriptor : extensionBundle) {
Collection<String> toRemove = new ArrayList<String>();
for (Iterator itr = resources.keys(); itr.hasNext();) {
String key = (String) itr.next();
if (key.startsWith("application." + descriptor.getId() + ".")) {
toRemove.add(key);
}
}
for (Iterator itr = toRemove.iterator(); itr.hasNext();) {
resources.removeKey((String) itr.next());
}
}
}
extensionBundles.clear();
extensionBundlesList.clear();
loadAll();
}
@SuppressWarnings("unchecked")
private void loadAll() throws Exception {
if (log.isInfoEnabled())
log.info("Loading applications");
if (!basedir.exists()) {
basedir.mkdirs();
}
// Load dev extensions first
loadDevExtensions();
File[] files = basedir.listFiles();
for (int index = 0; index < files.length; index++) {
try {
loadDir(files[index]);
} catch (Exception e) {
log.error("Failed to load " + files[index].getName(), e);
}
}
String descriptors = SystemProperties.get("adito.additionalDescriptors", "");
// Load any additional descriptors
for (StringTokenizer tokenizer = new StringTokenizer(descriptors, ","); tokenizer.hasMoreTokens();) {
File file = new File(tokenizer.nextToken());
if (file.exists()) {
try {
loadBundle(file, false);
} catch (Exception e) {
log.error("Failed to load " + file.getAbsolutePath(), e);
}
}
}
Collections.sort(extensionBundlesList);
}
private void loadDir(File dir) throws ExtensionException {
if (dir.isDirectory()) {
File[] descriptors = dir.listFiles(new FilenameFilter() {
public boolean accept(File f, String filename) {
return filename.equals("application.xml") || filename.equals("extension.xml");
}
});
if (descriptors.length == 0) {
log.warn("Extension folder " + dir.getName() + " found with no extension.xml (or the deprecated application.xml)");
return;
} else if (descriptors.length > 1) {
// Should never happen if its case sensitive
log.warn("Extension folder " + dir.getName()
+ " found with too many extension.xml (or deprecated application.xml) files. Please remove one. This extensions will be ignored.");
return;
}
if (log.isInfoEnabled())
log.info("Found application bundle " + dir.getName());
if (descriptors[0].getName().equals("application.xml")) {
log.warn("DEPRECATED. Application descriptor file " + descriptors[0]
+ " is no longer used, please use extension.xml instead.");
}
loadBundle(descriptors[0], false);
}
}
private void loadBundle(File descriptor, boolean devExtension) throws ExtensionException {
ExtensionBundle bundle = new ExtensionBundle(descriptor, devExtension);
loadBundle(bundle);
}
private void loadBundle(ExtensionBundle bundle) throws ExtensionException {
bundle.load();
ExtensionBundle oldBundle = (ExtensionBundle) extensionBundles.get(bundle.getId());
if (oldBundle != null && oldBundle.isDevExtension()) {
throw new ExtensionException(ExtensionException.CANNOT_REPLACE_DEV_EXTENSION, bundle.getId());
}
bundle.setCategory(ExtensionStore.INSTALLED_CATEGORY);
try {
ExtensionBundleStatus extensionStatus = ExtensionStoreStatusManager.getExtensionStatus(bundle.getId());
bundle.setStatus(extensionStatus);
} catch (IOException ioe) {
throw new ExtensionException(ExtensionException.INTERNAL_ERROR, ioe, "Failed to add bundle.");
}
for (ExtensionDescriptor descriptor : bundle) {
if (log.isInfoEnabled())
log.info("Extension " + descriptor.getName() + " has been loaded");
}
extensionBundlesList.remove(oldBundle);
extensionBundles.put(bundle.getId(), bundle);
extensionBundlesList.add(bundle);
}
/**
* @param bundleId
* @throws ExtensionException
* @throws IOException
*/
public void systemDisableExtension(String bundleId) throws ExtensionException, IOException {
ExtensionBundle extensionBundle = getExtensionBundle(bundleId);
extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.SYSTEM_DISABLED);
ExtensionStoreStatusManager.systemDisableExtension(bundleId);
}
/**
* @param bundleId
* @throws Exception
*/
public void disableExtension(String bundleId) throws Exception {
ExtensionBundle extensionBundle = getExtensionBundle(bundleId);
extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.DISABLED);
ExtensionStoreStatusManager.disableExtension(bundleId);
if(extensionBundle.isContainsPlugin()) {
extensionBundle.setType(ExtensionBundle.TYPE_PENDING_STATE_CHANGE);
}
}
/**
* @param bundleId
* @throws Exception
*/
public void enableExtension(String bundleId) throws Exception {
ExtensionBundle extensionBundle = getExtensionBundle(bundleId);
extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.ENABLED);
ExtensionStoreStatusManager.enableExtension(bundleId);
if(extensionBundle.isContainsPlugin()) {
extensionBundle.setType(ExtensionBundle.TYPE_PENDING_STATE_CHANGE);
}
}
private static void installExtension(ExtensionBundle extensionBundle) throws IOException {
if (ExtensionBundle.ExtensionBundleStatus.SYSTEM_DISABLED.equals(extensionBundle.getStatus())) {
ExtensionStoreStatusManager.installExtension(extensionBundle.getId());
extensionBundle.setStatus(ExtensionBundle.ExtensionBundleStatus.ENABLED);
}
}
/**
* @return List
*/
public List<ExtensionBundle> getExtensionBundles() {
return extensionBundlesList;
}
/**
* @param name
* @return true if the extension bundle has been loaded
*/
public boolean isExtensionBundleLoaded(String name) {
return extensionBundles.containsKey(name);
}
/**
* @return ExtensionDescriptor
*/
public ExtensionDescriptor getAgentApplication() {
try {
ExtensionBundle bundle = getExtensionBundle(AGENT_EXTENSION_BUNDLE_ID);
return bundle != null ? (ExtensionDescriptor) bundle.getApplicationDescriptor(AGENT_EXTENSION_BUNDLE_ID) : null;
} catch (Exception e) {
log.error("Failed to get agent descriptor. Loaded?", e);
return null;
}
}
/**
* @param id
* @return ExtensionDescriptor
*/
public ExtensionDescriptor getExtensionDescriptor(String id) {
for (ExtensionBundle bundle : extensionBundlesList) {
ExtensionDescriptor descriptor = bundle.getApplicationDescriptor(id);
if (descriptor != null && descriptor instanceof ExtensionDescriptor) {
return (ExtensionDescriptor) descriptor;
}
}
return null;
}
/**
* Get an extension bundle given its ID.
*
* @param id extension bundle id
* @return extension bundle
* @throws ExtensionException if bundle could not be located ({@link ExtensionException#INVALID_EXTENSION}).
*/
public ExtensionBundle getExtensionBundle(String id) throws ExtensionException {
if (!extensionBundles.containsKey(id)) {
throw new ExtensionException(ExtensionException.INVALID_EXTENSION, id);
}
return (ExtensionBundle) extensionBundles.get(id);
}
/**
* Reload all
*
* @throws Exception
*/
public void reload() throws Exception {
if (log.isInfoEnabled())
log.info("Reloading all application bundles");
boolean reconnect = downloadableExtensions != null;
downloadableExtensions = null;
downloadableExtensionsLastUpdated = null;
deregisterApplicationPermissions();
reloadAll();
if (reconnect) {
getDownloadableExtensionStoreDescriptor(true);
}
}
public static VersionInfo.Version getWorkingVersion() {
VersionInfo.Version version = new VersionInfo.Version(SystemProperties.get("adito.forceVersion", ContextHolder.getContext().getVersion().toString()));
return version;
}
/**
* @param id
* @throws ExtensionException
*/
@SuppressWarnings("unchecked")
public void reload(String id) throws ExtensionException {
if (log.isInfoEnabled())
log.info("Reloading application bundle " + id);
if (isExtensionLoaded(id)) {
ExtensionBundle bundle = getExtensionBundle(id);
try {
bundle.load();
} catch (ExtensionException ee) {
log.warn("Failed to reload extension descriptor.", ee);
extensionBundles.remove(id);
extensionBundlesList.remove(bundle);
throw ee;
}
} else {
loadDir(new File(basedir, id));
}
Collections.sort(extensionBundlesList);
}
/**
* @return File
* @throws IOException
*/
public File getUpdatedExtensionsDirectory() throws IOException {
File updatedExtensionsDir = new File(ContextHolder.getContext().getConfDirectory(), "updated-extensions");
if (!updatedExtensionsDir.exists() && !updatedExtensionsDir.mkdirs()) {
throw new IOException("The extension update directory " + updatedExtensionsDir.getAbsolutePath()
+ " could not be created.");
}
return updatedExtensionsDir;
}
/**
* Remove an extension bundle. The bundle will be stopped if it is started
* and events will be fired. Global warnings will also be created if the
* bundle contains any plugins informing the administrator the server must
* be restarted.
*
* @param bundle bundle to remove
* @throws Exception on any error
*/
@SuppressWarnings("unchecked")
public void removeExtensionBundle(ExtensionBundle bundle) throws Exception {
if (log.isInfoEnabled())
log.info("Removing extension bundle " + bundle.getId());
boolean containsPlugin = bundle.isContainsPlugin();
try {
CoreServlet.getServlet()
.fireCoreEvent(new CoreEvent(this,
CoreEventConstants.REMOVING_EXTENSION,
bundle,
null,
CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
bundle.getId())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName()));
bundle.removeBundle();
VERSION_PREFS.remove(bundle.getId());
CoreServlet.getServlet()
.fireCoreEvent(new CoreEvent(this,
CoreEventConstants.REMOVE_EXTENSION,
bundle,
null,
CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
bundle.getId())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName()));
if (containsPlugin) {
if (!ExtensionStore.getInstance().isRepositoryBacked()) {
if (log.isInfoEnabled())
log.info("Extension " + bundle.getId() + " contains plugins, deferring removal until restart.");
StringBuffer toRemove = new StringBuffer(STORE_PREF.get(DIRS_TO_REMOVE, ""));
if (toRemove.length() > 0) {
toRemove.append(",");
}
toRemove.append(bundle.getBaseDir());
STORE_PREF.put(DIRS_TO_REMOVE, toRemove.toString());
}
}
ExtensionStoreStatusManager.removeExtension(bundle.getId());
} catch (Exception e) {
CoreServlet.getServlet()
.fireCoreEvent(new CoreEvent(this, CoreEventConstants.REMOVE_EXTENSION, null, null, e).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
bundle.getId())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName()));
throw e;
} finally {
if (!containsPlugin) {
extensionBundles.remove(bundle.getId());
extensionBundlesList.remove(bundle);
if (log.isInfoEnabled())
log.info("Extension Zip file " + bundle.getId() + ".zip" + " has been deleted.");
}
if (ExtensionStore.getInstance().isRepositoryBacked()) {
RepositoryFactory.getRepository().getStore(ExtensionStore.ARCHIVE_STORE).removeEntry(bundle.getId() + ".zip");
}
Collections.sort(extensionBundlesList);
}
}
/**
* Determine if an extension is installed given its extension bundle id.
*
* @param id extension bundle id
* @return true if the extension has been loaded
*/
public boolean isExtensionLoaded(String id) {
for (Iterator i = extensionBundlesList.iterator(); i.hasNext();) {
ExtensionBundle bundle = (ExtensionBundle) i.next();
if (bundle.containsApplication(id)) {
return true;
}
}
return false;
}
/**
* Install an extension directly from the Adito Extension Store given its id
* and an input stream providing the extension bundle archive in zip format.
* This method will also attempt to install all of the bundles dependencies.
*
* @param id extension bundle id
* @param in input stream of extension bundle archive (Zip format)
* @param request request object
* @param contentLength size of extension bundle if known (-1 when unavailable - used for progress bar)
* @return ExtensionBundle installed extension bundle
* @throws ExtensionException on any error
*/
public ExtensionBundle installExtensionFromStore(final String id, InputStream in, HttpServletRequest request, long contentLength)
throws ExtensionException {
ExtensionStoreDescriptor store;
try {
// Get the application store descriptor
store = getDownloadableExtensionStoreDescriptor(true);
if (store == null) {
throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "No downloadable applications.");
}
ExtensionBundle bundle = store.getApplicationBundle(id);
if (bundle == null) {
throw new ExtensionException(ExtensionException.INVALID_EXTENSION, id);
}
// Check host version
Context context = ContextHolder.getContext();
if (bundle.getRequiredHostVersion() != null && bundle.getRequiredHostVersion().compareTo(context.getVersion()) > 0) {
throw new ExtensionException(ExtensionException.INSUFFICIENT_ADITO_HOST_VERSION,
bundle.getId(),
bundle.getRequiredHostVersion().toString());
}
// Install all dependencies
if (bundle.getDependencies() != null) {
for (String dep : bundle.getDependencies()) {
if (isExtensionBundleLoaded(dep)) {
ExtensionBundle current = getExtensionBundle(dep);
ExtensionBundle available = store.getApplicationBundle(dep);
if(available != null) {
if (!current.isDevExtension() && isNewerVersionAvailable(available, current)) {
if (log.isInfoEnabled())
log.info("Found a dependency (" + dep + "), that needs upgrading. " + current.getVersion().toString() + " is the current version, " + available.getVersion().toString() + " is available. Installing now");
installExtensionFromStore(current.getId(), available.getVersion().toString(), request);
}
}
} else {
try {
if (log.isInfoEnabled())
log.info("Found a dependency (" + dep + "), that is not installed. Installing now");
installExtensionFromStore(dep, store.getApplicationBundle(dep).getVersion().toString(), request);
} catch (Exception e) {
throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "Failed to install dependency " + dep);
}
}
}
}
// This action may be wrapped in a task progress monitor
Task task = (Task)request.getAttribute(TaskHttpServletRequest.ATTR_TASK);
if(task != null && request.getAttribute(TaskHttpServletRequest.ATTR_TASK_PROGRESS_HANDLED_EXTERNALLY) == null) {
TaskProgressBar bar = new TaskProgressBar("installExtension", 0, (int)contentLength, 0); // TODO should accept longs
task.clearProgressBars();
task.addProgressBar(bar);
in = new TaskInputStream(bar, in);
((TaskInputStream)in).getProgressBar().setNote(new BundleActionMessage("extensions", "taskProgress.downloadExtension.note", id));
if(!task.isConfigured())
task.configured();
}
return installExtension(id, in);
} catch (IOException jde) {
throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "Failed to load descriptor.");
} catch (JDOMException jde) {
throw new ExtensionException(ExtensionException.FAILED_TO_PARSE_DESCRIPTOR);
}
}
/**
* Install an extension directly from the Adito Extension Store given its id.
* This method will also attempt to install all of the bundles dependencies.
*
* @param id extension bundle ID
* @param version
* @param request request object
* @throws ExtensionException extension errors
* @throws IOException io errors
*/
public void installExtensionFromStore(String id, String version, HttpServletRequest request) throws IOException, ExtensionException {
URLConnection connection = downloadExtension(id, version);
InputStream inputStream = connection.getInputStream();
installExtensionFromStore(id, inputStream, request, connection.getContentLength());
}
/**
* Install an extension givens its bundle and an input stream providing the
* extension bundle archive in zip format.
*
* @param id extension bundle ID
* @param in input stream provided the extension bundle archive (in Zip
* format)
* @return ExtensionBundle
* @throws IOException io errors
* @throws ExtensionException extension errors
*/
public ExtensionBundle installExtension(final String id, InputStream in) throws IOException, ExtensionException {
streamToRepositoryStore(in, id);
try {
RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
ZipExtract.extractZipFile(getExtensionStoreDirectory(), repStore.getEntryInputStream(id + ".zip"));
reload(id);
ExtensionBundle newBundle = getExtensionBundle(id);
installExtension(newBundle);
fireBundleEvent(CoreEventConstants.INSTALL_EXTENSION, newBundle);
} catch (IOException e) {
CoreServlet.getServlet().fireCoreEvent(new CoreEvent(this,
CoreEventConstants.INSTALL_EXTENSION,
null,
null,
CoreEvent.STATE_UNSUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID, id));
throw e;
} catch (ExtensionException e) {
CoreServlet.getServlet().fireCoreEvent(new CoreEvent(this,
CoreEventConstants.INSTALL_EXTENSION,
null,
null,
CoreEvent.STATE_UNSUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID, id));
throw e;
}
return getExtensionBundle(id);
}
/**
* Check the license for a given bundle, adding a page redirect to ask the
* user to accept a license agreement if required.
* <p>
* This should be called just after an extenion is installed either from the
* Adito Application Store or manually.
*
* @param newBundle newly installed bundle
* @param request request object
* @param installedForward the forward to redirect to after license
* agreement has been show
* @throws Exception on any error
*/
public void licenseCheck(final ExtensionBundle newBundle, HttpServletRequest request, final ActionForward installedForward)
throws Exception {
final RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
// If installing, there may be a license agreement to handle
File licenseFile = newBundle.getLicenseFile();
if (licenseFile != null && licenseFile.exists()) {
LicenseAgreement licenseAgreement = getLicenseAgreement(newBundle, repStore, licenseFile, installedForward);
CoreUtil.requestLicenseAgreement(request.getSession(), licenseAgreement);
}
}
/**
* Invoked after an extension has been installed, this method adds a global
* warning if the bundle contains any plugins indicating a restart is neeed.
* Also, if the bundle doesn't contain any plugins it will immediately run
* the 'installer'.
*
* @param newBundle newly installed extension bundle
* @param request request
* @throws Exception on any error
*/
public void postInstallExtension(final ExtensionBundle newBundle, HttpServletRequest request) throws Exception {
boolean containsPlugin = newBundle.isContainsPlugin();
if (containsPlugin) {
GlobalWarningManager.getInstance().addMultipleGlobalWarning(new GlobalWarning(GlobalWarning.MANAGEMENT_USERS, new BundleActionMessage("extensions",
"extensionStore.message.pluginInstalledRestartRequired"), DismissType.DISMISS_FOR_USER));
newBundle.setType(ExtensionBundle.TYPE_PENDING_INSTALLATION);
} else {
newBundle.start();
// Installer must be run after start as it may have custom tasks
if (newBundle.getInstaller() != null) {
newBundle.getInstaller().doInstall(null);
}
newBundle.activate();
}
}
/**
* Update an extension givens its bundle and an input stream providing the
* extension bundle archive in zip format.
*
* @param id extension bundle id
* @param in input stream of extension bundle archive (Zip format)
* @param request reuqest object
* @param contentLength content length
* @return ExtensionBundle newly loaded extension bundle
* @throws Exception on any error
*/
public ExtensionBundle updateExtension(String id, InputStream in, HttpServletRequest request, long contentLength) throws Exception {
ExtensionStoreDescriptor store;
// Get the application store descriptor
/**
* LDP - Why does this require the extension store descriptor? Extensions should be
* updatable even if the extension store is not available!!!!!!!!
*/
// store = getDownloadableExtensionStoreDescriptor(true);
// if (store == null) {
// throw new ExtensionException(ExtensionException.INTERNAL_ERROR, "No downloadable applications.");
// }
// This action may be wrapped in a task progress monitor
Task task = (Task)request.getAttribute(TaskHttpServletRequest.ATTR_TASK);
if(task != null && request.getAttribute(TaskHttpServletRequest.ATTR_TASK_PROGRESS_HANDLED_EXTERNALLY) == null) {
TaskProgressBar bar = new TaskProgressBar("updateExtension", 0, (int)contentLength, 0);
task.addProgressBar(bar);
in = new TaskInputStream(bar, in);
((TaskInputStream)in).getProgressBar().setNote(new BundleActionMessage("extensions", "taskProgress.downloadExtension.note", id));
task.configured();
}
ExtensionBundle currentBundle = getExtensionBundle(id);
if (currentBundle == null) {
throw new ExtensionException(ExtensionException.INVALID_EXTENSION, id);
}
try {
return updateExtension(currentBundle, in, request);
} catch (ExtensionException ee) {
CoreServlet.getServlet()
.fireCoreEvent(new CoreEvent(this,
CoreEventConstants.UPDATE_EXTENSION,
null,
null,
CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
currentBundle.getId())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, currentBundle.getName())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_VERSION,
currentBundle.getVersion().toString()));
throw ee;
}
}
private ExtensionBundle updateExtension(ExtensionBundle currentBundle, InputStream in,
HttpServletRequest request) throws Exception {
// Check host version
Context context = ContextHolder.getContext();
if (currentBundle.getRequiredHostVersion() != null && currentBundle.getRequiredHostVersion().compareTo(context.getVersion()) > 0) {
throw new ExtensionException(ExtensionException.INSUFFICIENT_ADITO_HOST_VERSION,
currentBundle.getId(),
currentBundle.getRequiredHostVersion().toString());
}
boolean containsPlugin = currentBundle.isContainsPlugin();
// Remove Extension Bundle;
try {
currentBundle.removeBundle();
VERSION_PREFS.remove(currentBundle.getId());
} catch (Exception e) {
throw e;
} finally {
if (!containsPlugin) {
extensionBundles.remove(currentBundle.getId());
extensionBundlesList.remove(currentBundle);
if (log.isInfoEnabled())
log.info("Extension Zip file " + currentBundle.getId() + ".zip" + " has been deleted.");
}
}
// Install Extension;
streamToRepositoryStore(in, currentBundle.getId());
RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
ZipExtract.extractZipFile(getExtensionStoreDirectory(), repStore.getEntryInputStream(currentBundle.getId() + ".zip"));
if (containsPlugin) {
currentBundle.setType(ExtensionBundle.TYPE_PENDING_UPDATE);
fireBundleEvent(CoreEventConstants.UPDATE_EXTENSION, currentBundle);
return currentBundle;
} else {
reload(currentBundle.getId());
ExtensionBundle newBundle = getExtensionBundle(currentBundle.getId());
installExtension(newBundle);
postInstallExtension(newBundle, request);
if (newBundle.getStatus().isStartedOrActivated()) {
newBundle.stop();
newBundle.start();
newBundle.activate();
}
fireBundleEvent(CoreEventConstants.UPDATE_EXTENSION, newBundle);
return newBundle;
}
}
/**
* Set the <i>Edition</i>, i.e. GPL, Community or Enterprise.
*
* @param currentEdition edition
*/
public static void setCurrentEdition(String currentEdition) {
ExtensionStore.currentEdition = currentEdition;
}
private LicenseAgreement getLicenseAgreement(final ExtensionBundle newBundle, final RepositoryStore repStore,
final File licenseFile, final ActionForward installedForward) {
return new LicenseAgreement(newBundle.getName(), licenseFile, new LicenseAgreementCallback() {
public void licenseAccepted(HttpServletRequest request) {
// Dont care
}
public void licenseRejected(HttpServletRequest request) {
// Remove the repository entry if it is in
// use
if (isRepositoryBacked()) {
try {
repStore.removeEntry(newBundle.getId() + ".zip");
} catch (IOException ex) {
}
}
// Remove the expanded bundle
if (newBundle.getBaseDir().exists()) {
Util.delTree(newBundle.getBaseDir());
}
// Reload the extension store
try {
reload(newBundle.getId());
} catch (Exception e) {
log.error("Failed to reload extension store.");
}
}
}, installedForward);
}
private static boolean isNewerVersionAvailable(ExtensionBundle available, ExtensionBundle current) {
VersionInfo.Version v1 = new VersionInfo.Version(available.getVersion().toString());
VersionInfo.Version v2 = new VersionInfo.Version(current.getVersion().toString());
return v1.compareTo(v2) > 0;
}
private static void streamToRepositoryStore(InputStream in, String bundleId) throws IOException {
RepositoryStore repStore = RepositoryFactory.getRepository().getStore(ARCHIVE_STORE);
OutputStream out = null;
try {
out = repStore.getEntryOutputStream(bundleId + ".zip");
Util.copy(in, out);
} finally {
Util.closeStream(in);
Util.closeStream(out);
}
}
private void fireBundleEvent(int eventType, ExtensionBundle bundle) {
String extensionType = getExtensionType(bundle);
CoreServlet.getServlet()
.fireCoreEvent(new CoreEvent(this, eventType, null, null, CoreEvent.STATE_SUCCESSFUL).addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_ID,
bundle.getId())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_NAME, bundle.getName())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_VERSION,
bundle.getVersion().toString())
.addAttribute(CoreAttributeConstants.EVENT_ATTR_EXTENSION_TYPE, extensionType));
}
private static String getExtensionType(ExtensionBundle bundle) {
for (Iterator itr = bundle.iterator(); itr.hasNext();) {
ExtensionDescriptor descriptor = (ExtensionDescriptor) itr.next();
if (descriptor.getExtensionType() instanceof ExtensionType) {
return descriptor.getExtensionType().getType();
}
}
return null;
}
private void addAdditionalWebResource() {
//
String additionalWebResources = SystemProperties.get("adito.additionalWebResourceDirectories", "");
if (additionalWebResources != null) {
StringTokenizer t = new StringTokenizer(additionalWebResources, ",");
while (t.hasMoreTokens()) {
try {
URL u = null;
String dir = t.nextToken();
if (dir.endsWith("]")) {
int idx = dir.indexOf('[');
if (idx != -1) {
dir = dir.substring(0, idx);
u = new File(dir).getCanonicalFile().toURL();
log.warn("Associating additional web resource directories with plugins is no longer supported.");
}
}
if (u == null) {
u = new File(dir).getCanonicalFile().toURL();
}
ContextHolder.getContext().addResourceBase(u);
} catch (Exception e) {
log.error("Failed to add additional web resources directory.", e);
}
}
}
}
private void addAdditionalClasspath() {
// Add any additional class path elements
StringTokenizer t = new StringTokenizer(SystemProperties.get("adito.additionalClasspath", ""), ",");
while (t.hasMoreTokens()) {
try {
String sf = t.nextToken();
File[] f = null;
if (sf.endsWith("/*.jar")) {
f = new File(sf.substring(0, sf.length() - 6)).listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().toLowerCase().endsWith(".jar");
}
});
} else {
f = new File[1];
f[0] = new File(sf);
}
for (int j = 0; f != null && j < f.length; j++) {
if (f[j].exists() && (f[j].isDirectory() || f[j].getName().toLowerCase().endsWith(".jar"))) {
URL u = f[j].toURL();
ContextHolder.getContext().addContextLoaderURL(u);
}
}
} catch (MalformedURLException murle) {
log.warn("Invalid element in additional classpaths");
}
}
}
private void initialiseRepository() {
RepositoryStore store = RepositoryFactory.getRepository().getStore("archives");
// Remove the existing extensions
if (basedir.exists()) {
Util.delTree(basedir);
}
// Now recreate all extensions from the repository
basedir.mkdirs();
String[] archives = store.listEntries();
for (int i = 0; i < archives.length; i++) {
if (log.isInfoEnabled()) {
log.info("Extracting archive " + archives[i]);
}
try {
ZipExtract.extractZipFile(basedir, store.getEntryInputStream(archives[i]));
if (log.isInfoEnabled()) {
log.info("Completed archive extraction for extension " + archives[i]);
}
} catch (IOException ex) {
log.error("Error extracting archive for extension " + archives[i], ex);
Util.delTree(new File(basedir, archives[i]));
}
}
}
private void loadDevExtensions() {
List<String> devExtensions = new ArrayList<String>();
String extensionList = SystemProperties.get("adito.devExtensions", "");
StringTokenizer t = new StringTokenizer(extensionList, ",");
while (t.hasMoreTokens()) {
String ext = t.nextToken();
if (ext.equalsIgnoreCase("all")) {
File f = new File(SystemProperties.get("user.dir")).getParentFile();
File[] dirs = f.listFiles(new FileFilter() {
public boolean accept(File pathname) {
File f = new File(pathname, "extensions");
return f.exists() && f.isDirectory();
}
});
for (int i = 0; dirs != null && i < dirs.length; i++) {
devExtensions.add(dirs[i].getName());
}
} else if (ext.equalsIgnoreCase("enterprise")) {
File f = new File(SystemProperties.get("user.dir")).getParentFile();
File[] dirs = f.listFiles(new FileFilter() {
public boolean accept(File pathname) {
File f = new File(pathname, "extensions");
return f.exists() && f.isDirectory() && pathname.getName().indexOf("adito-enterprise-") != -1;
}
});
for (int i = 0; dirs != null && i < dirs.length; i++) {
devExtensions.add(dirs[i].getName());
}
} else if (ext.equalsIgnoreCase("community")) {
File f = new File(SystemProperties.get("user.dir")).getParentFile();
File[] dirs = f.listFiles(new FileFilter() {
public boolean accept(File pathname) {
File f = new File(pathname, "extensions");
return f.exists() && f.isDirectory() && pathname.getName().indexOf("adito-community-") != -1;
}
});
for (int i = 0; dirs != null && i < dirs.length; i++) {
devExtensions.add(dirs[i].getName());
}
} else {
if (ext.startsWith("!")) {
devExtensions.remove(ext.substring(1));
} else {
devExtensions.add(ext);
}
}
}
for (Iterator it = devExtensions.iterator(); it.hasNext();) {
String ext = (String) it.next();
File d = new File(new File(SystemProperties.get("user.dir")).getParentFile(), ext);
File extensionDir = new File(new File(d, "extensions"), d.getName());
File extensionDescriptor = new File(extensionDir, "extension.xml");
if (extensionDescriptor.exists()) {
try {
loadBundle(extensionDescriptor, true);
} catch (Exception e) {
log.error("Failed to load dev extension " + extensionDescriptor.getAbsolutePath(), e);
}
}
}
}
class BundleComparator implements Comparator<ExtensionBundle> {
public int compare(ExtensionBundle o1, ExtensionBundle o2) {
int i = new Integer(o1.getOrder()).compareTo(new Integer(o2.getOrder()));
return i == 0 ? o1.getId().compareTo(o2.getId()) : i;
}
}
}