| /******************************************************************************* |
| * Copyright (c) 2007, 2009 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.simpleconfigurator.manipulator; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.*; |
| import org.eclipse.core.runtime.URIUtil; |
| import org.eclipse.equinox.internal.frameworkadmin.equinox.ParserUtils; |
| import org.eclipse.equinox.internal.frameworkadmin.utils.Utils; |
| import org.eclipse.equinox.internal.provisional.frameworkadmin.*; |
| import org.eclipse.equinox.internal.provisional.simpleconfigurator.manipulator.SimpleConfiguratorManipulator; |
| import org.eclipse.equinox.internal.simpleconfigurator.utils.SimpleConfiguratorUtils; |
| import org.osgi.framework.Constants; |
| |
| /** |
| * |
| */ |
| public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorManipulator { |
| class LocationInfo { |
| URI[] prerequisiteLocations = null; |
| URI systemBundleLocation = null; |
| URI[] systemFragmentedBundleLocations = null; |
| } |
| |
| private final static boolean DEBUG = false; |
| |
| private static final BundleInfo[] NULL_BUNDLEINFOS = new BundleInfo[0]; |
| |
| public static final String PROP_KEY_EXCLUSIVE_INSTALLATION = "org.eclipse.equinox.simpleconfigurator.exclusiveInstallation"; //$NON-NLS-1$ |
| public static final String CONFIG_LIST = "bundles.info"; //$NON-NLS-1$ |
| public static final String CONFIGURATOR_FOLDER = "org.eclipse.equinox.simpleconfigurator"; //$NON-NLS-1$ |
| public static final String PROP_KEY_CONFIGURL = "org.eclipse.equinox.simpleconfigurator.configUrl"; //$NON-NLS-1$ |
| |
| private Set manipulators = new HashSet(); |
| |
| /** |
| * Return the ConfiguratorConfigFile which is determined |
| * by the parameters set in Manipulator. |
| * |
| * @param manipulator |
| * @return File |
| */ |
| private static File getConfigFile(Manipulator manipulator) throws IllegalStateException { |
| File fwConfigLoc = manipulator.getLauncherData().getFwConfigLocation(); |
| File baseDir = null; |
| if (fwConfigLoc == null) { |
| baseDir = manipulator.getLauncherData().getHome(); |
| if (baseDir == null) { |
| if (manipulator.getLauncherData().getLauncher() != null) { |
| baseDir = manipulator.getLauncherData().getLauncher().getParentFile(); |
| } else { |
| throw new IllegalStateException("All of fwConfigFile, home, launcher are not set."); |
| } |
| } |
| } else { |
| if (fwConfigLoc.exists()) |
| if (fwConfigLoc.isDirectory()) |
| baseDir = fwConfigLoc; |
| else |
| baseDir = fwConfigLoc.getParentFile(); |
| else { |
| // TODO We need to decide whether launcher data configLocation is the location of a file or a directory |
| if (fwConfigLoc.getName().endsWith(".ini")) //$NON-NLS-1$ |
| baseDir = fwConfigLoc.getParentFile(); |
| else |
| baseDir = fwConfigLoc; |
| } |
| } |
| File configuratorFolder = new File(baseDir, SimpleConfiguratorManipulatorImpl.CONFIGURATOR_FOLDER); |
| File targetFile = new File(configuratorFolder, SimpleConfiguratorManipulatorImpl.CONFIG_LIST); |
| if (!Utils.createParentDir(targetFile)) |
| return null; |
| return targetFile; |
| } |
| |
| static boolean isPrerequisiteBundles(URI location, LocationInfo info) { |
| boolean ret = false; |
| |
| if (info.prerequisiteLocations == null) |
| return false; |
| for (int i = 0; i < info.prerequisiteLocations.length; i++) |
| if (location.equals(info.prerequisiteLocations[i])) { |
| ret = true; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static boolean isSystemBundle(URI location, LocationInfo info) { |
| if (info.systemBundleLocation == null) |
| return false; |
| if (location.equals(info.systemBundleLocation)) |
| return true; |
| return false; |
| } |
| |
| static boolean isSystemFragmentBundle(URI location, LocationInfo info) { |
| boolean ret = false; |
| if (info.systemFragmentedBundleLocations == null) |
| return false; |
| for (int i = 0; i < info.systemFragmentedBundleLocations.length; i++) |
| if (location.equals(info.systemFragmentedBundleLocations[i])) { |
| ret = true; |
| break; |
| } |
| return ret; |
| } |
| |
| private static boolean isTargetConfiguratorBundle(BundleInfo[] bInfos) { |
| for (int i = 0; i < bInfos.length; i++) { |
| if (isTargetConfiguratorBundle(bInfos[i].getLocation())) { |
| return true; |
| //TODO confirm that startlevel of configurator bundle must be no larger than beginning start level of fw. However, there is no way to know the start level of cached ones. |
| } |
| } |
| return false; |
| } |
| |
| private static boolean isTargetConfiguratorBundle(URI location) { |
| final String symbolic = Utils.getPathFromClause(Utils.getManifestMainAttributes(location, Constants.BUNDLE_SYMBOLICNAME)); |
| return (SimpleConfiguratorManipulator.SERVICE_PROP_VALUE_CONFIGURATOR_SYMBOLICNAME.equals(symbolic)); |
| } |
| |
| private void algorithm(int initialSl, SortedMap bslToList, BundleInfo configuratorBInfo, List setToInitialConfig, List setToSimpleConfig, LocationInfo info) { |
| int configuratorSL = configuratorBInfo.getStartLevel(); |
| |
| Integer sL0 = (Integer) bslToList.keySet().iterator().next();// StartLevel == 0; |
| List list0 = (List) bslToList.get(sL0); |
| if (sL0.intValue() == 0) |
| for (Iterator ite2 = list0.iterator(); ite2.hasNext();) { |
| BundleInfo bInfo = (BundleInfo) ite2.next(); |
| if (isSystemBundle(bInfo.getLocation(), info)) { |
| setToSimpleConfig.add(bInfo); |
| break; |
| } |
| } |
| |
| for (Iterator ite = bslToList.keySet().iterator(); ite.hasNext();) { |
| Integer sL = (Integer) ite.next(); |
| List list = (List) bslToList.get(sL); |
| |
| if (sL.intValue() < configuratorSL) { |
| for (Iterator ite2 = list.iterator(); ite2.hasNext();) { |
| BundleInfo bInfo = (BundleInfo) ite2.next(); |
| if (!isSystemBundle(bInfo.getLocation(), info)) |
| setToInitialConfig.add(bInfo); |
| } |
| } else if (sL.intValue() > configuratorSL) { |
| for (Iterator ite2 = list.iterator(); ite2.hasNext();) { |
| BundleInfo bInfo = (BundleInfo) ite2.next(); |
| if (isPrerequisiteBundles(bInfo.getLocation(), info) || isSystemFragmentBundle(bInfo.getLocation(), info)) |
| if (!isSystemBundle(bInfo.getLocation(), info)) |
| setToInitialConfig.add(bInfo); |
| setToSimpleConfig.add(bInfo); |
| } |
| } else { |
| boolean found = false; |
| for (Iterator ite2 = list.iterator(); ite2.hasNext();) { |
| BundleInfo bInfo = (BundleInfo) ite2.next(); |
| if (found) { |
| if (!isSystemBundle(bInfo.getLocation(), info)) |
| if (isPrerequisiteBundles(bInfo.getLocation(), info) || isSystemFragmentBundle(bInfo.getLocation(), info)) |
| setToInitialConfig.add(bInfo); |
| setToSimpleConfig.add(bInfo); |
| continue; |
| } |
| if (isTargetConfiguratorBundle(bInfo.getLocation())) |
| found = true; |
| else if (!isSystemBundle(bInfo.getLocation(), info)) |
| setToInitialConfig.add(bInfo); |
| setToSimpleConfig.add(bInfo); |
| } |
| } |
| } |
| |
| setToInitialConfig.add(configuratorBInfo); |
| } |
| |
| private boolean checkResolve(BundleInfo bInfo, BundlesState state) {//throws ManipulatorException { |
| if (bInfo == null) |
| throw new IllegalArgumentException("bInfo is null."); |
| |
| if (!state.isResolved()) |
| state.resolve(false); |
| // if (DEBUG) |
| // System.out.println(state.toString()); |
| |
| if (!state.isResolved(bInfo)) { |
| printoutUnsatisfiedConstraints(bInfo, state); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean divideBundleInfos(Manipulator manipulator, List setToInitialConfig, List setToSimpleConfig, final int initialBSL) throws IOException { |
| BundlesState state = manipulator.getBundlesState(); |
| BundleInfo[] targetBundleInfos = null; |
| if (state.isFullySupported()) { |
| targetBundleInfos = state.getExpectedState(); |
| } else { |
| targetBundleInfos = manipulator.getConfigData().getBundles(); |
| } |
| BundleInfo configuratorBInfo = null; |
| for (int i = 0; i < targetBundleInfos.length; i++) { |
| if (isTargetConfiguratorBundle(targetBundleInfos[i].getLocation())) { |
| if (targetBundleInfos[i].isMarkedAsStarted()) { |
| configuratorBInfo = targetBundleInfos[i]; |
| break; |
| } |
| } |
| } |
| if (configuratorBInfo == null && !manipulators.contains(manipulator)) { |
| return false; |
| } else if (manipulators.contains(manipulator) && targetBundleInfos.length == 0) { |
| // Resulting state will have no bundles - so is an uninstall, including |
| // uninstall of the configurator. However, we have seen this manipulator |
| // before with a target configurator bundle, so allow uninstall to proceed, |
| // but only get one chance. |
| manipulators.remove(manipulator); |
| } else if (!manipulators.contains(manipulator)) { |
| manipulators.add(manipulator); |
| } |
| |
| if (state.isFullySupported()) { |
| state.resolve(false); |
| } |
| |
| LocationInfo info = new LocationInfo(); |
| setSystemBundles(state, info); |
| if (configuratorBInfo != null) { |
| setPrerequisiteBundles(configuratorBInfo, state, info); |
| SortedMap bslToList = getSortedMap(initialBSL, targetBundleInfos); |
| algorithm(initialBSL, bslToList, configuratorBInfo, setToInitialConfig, setToSimpleConfig, info); |
| } |
| return true; |
| } |
| |
| private SortedMap getSortedMap(int initialSl, BundleInfo[] bInfos) { |
| SortedMap bslToList = new TreeMap(); |
| for (int i = 0; i < bInfos.length; i++) { |
| Integer sL = new Integer(bInfos[i].getStartLevel()); |
| if (sL.intValue() == BundleInfo.NO_LEVEL) |
| sL = new Integer(initialSl); |
| List list = (List) bslToList.get(sL); |
| if (list == null) { |
| list = new LinkedList(); |
| bslToList.put(sL, list); |
| } |
| list.add(bInfos[i]); |
| } |
| return bslToList; |
| } |
| |
| private BundleInfo[] orderingInitialConfig(List setToInitialConfig) { |
| List notToBeStarted = new LinkedList(); |
| List toBeStarted = new LinkedList(); |
| for (Iterator ite2 = setToInitialConfig.iterator(); ite2.hasNext();) { |
| BundleInfo bInfo = (BundleInfo) ite2.next(); |
| if (bInfo.isMarkedAsStarted()) |
| toBeStarted.add(bInfo); |
| else |
| notToBeStarted.add(bInfo); |
| } |
| setToInitialConfig.clear(); |
| setToInitialConfig.addAll(notToBeStarted); |
| setToInitialConfig.addAll(toBeStarted); |
| return Utils.getBundleInfosFromList(setToInitialConfig); |
| |
| } |
| |
| private void printoutUnsatisfiedConstraints(BundleInfo bInfo, BundlesState state) { |
| if (DEBUG) { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("Missing constraints:\n"); |
| String[] missings = state.getUnsatisfiedConstraints(bInfo); |
| for (int i = 0; i < missings.length; i++) |
| sb.append(" " + missings[i] + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| System.out.println(sb.toString()); |
| } |
| } |
| |
| public BundleInfo[] loadConfiguration(URL url, File base) throws IOException { |
| if (url == null) |
| return NULL_BUNDLEINFOS; |
| |
| List simpleBundles = SimpleConfiguratorUtils.readConfiguration(url, base.toURI()); |
| |
| // convert to FrameworkAdmin BundleInfo Type |
| BundleInfo[] result = new BundleInfo[simpleBundles.size()]; |
| int i = 0; |
| for (Iterator iterator = simpleBundles.iterator(); iterator.hasNext();) { |
| org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo simpleInfo = (org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo) iterator.next(); |
| URI location = simpleInfo.getLocation(); |
| if (!location.isAbsolute() && simpleInfo.getBaseLocation() != null) |
| location = URIUtil.makeAbsolute(location, simpleInfo.getBaseLocation()); |
| |
| BundleInfo bundleInfo = new BundleInfo(simpleInfo.getSymbolicName(), simpleInfo.getVersion(), location, simpleInfo.getStartLevel(), simpleInfo.isMarkedAsStarted()); |
| result[i++] = bundleInfo; |
| } |
| return result; |
| } |
| |
| public void saveConfiguration(BundleInfo[] configuration, File outputFile, File base) throws IOException { |
| saveConfiguration(configuration, outputFile, base, false); |
| } |
| |
| private void saveConfiguration(BundleInfo[] configuration, File outputFile, File base, boolean backup) throws IOException { |
| if (backup && outputFile.exists()) { |
| File backupFile = Utils.getSimpleDataFormattedFile(outputFile); |
| if (!outputFile.renameTo(backupFile)) { |
| throw new IOException("Fail to rename from (" + outputFile + ") to (" + backupFile + ")"); |
| } |
| } |
| |
| // convert to SimpleConfigurator BundleInfo Type |
| org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo[] simpleInfos = new org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo[configuration.length]; |
| for (int i = 0; i < configuration.length; i++) { |
| BundleInfo bundleInfo = configuration[i]; |
| String symbolicName = bundleInfo.getSymbolicName(); |
| String bundleVersion = bundleInfo.getVersion(); |
| URI location = base != null ? URIUtil.makeRelative(bundleInfo.getLocation(), base.toURI()) : bundleInfo.getLocation(); |
| if (symbolicName == null || bundleVersion == null || location == null) |
| throw new IllegalArgumentException("Cannot persist bundleinfo: " + bundleInfo.toString()); |
| simpleInfos[i] = new org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo(symbolicName, bundleVersion, location, bundleInfo.getStartLevel(), bundleInfo.isMarkedAsStarted()); |
| } |
| SimpleConfiguratorManipulatorUtils.writeConfiguration(simpleInfos, outputFile); |
| } |
| |
| public BundleInfo[] save(Manipulator manipulator, boolean backup) throws IOException { |
| List setToInitialConfig = new LinkedList(); |
| List setToSimpleConfig = new LinkedList(); |
| ConfigData configData = manipulator.getConfigData(); |
| |
| if (!divideBundleInfos(manipulator, setToInitialConfig, setToSimpleConfig, configData.getInitialBundleStartLevel())) |
| return configData.getBundles(); |
| |
| File outputFile = getConfigFile(manipulator); |
| saveConfiguration((BundleInfo[]) setToSimpleConfig.toArray(new BundleInfo[setToSimpleConfig.size()]), outputFile, ParserUtils.getOSGiInstallArea(Arrays.asList(manipulator.getLauncherData().getProgramArgs()), manipulator.getConfigData().getProperties(), manipulator.getLauncherData()), backup); |
| configData.setProperty(SimpleConfiguratorManipulatorImpl.PROP_KEY_CONFIGURL, outputFile.toURL().toExternalForm()); |
| return orderingInitialConfig(setToInitialConfig); |
| } |
| |
| void setPrerequisiteBundles(BundleInfo configuratorBundleInfo, BundlesState state, LocationInfo info) { |
| if (state.isFullySupported()) |
| if (!this.checkResolve(configuratorBundleInfo, state)) { |
| printoutUnsatisfiedConstraints(configuratorBundleInfo, state); |
| return; |
| } |
| BundleInfo[] prerequisites = state.getPrerequisteBundles(configuratorBundleInfo); |
| info.prerequisiteLocations = new URI[prerequisites.length]; |
| for (int i = 0; i < prerequisites.length; i++) |
| info.prerequisiteLocations[i] = prerequisites[i].getLocation(); |
| return; |
| |
| } |
| |
| void setSystemBundles(BundlesState state, LocationInfo info) { |
| BundleInfo systemBundleInfo = state.getSystemBundle(); |
| if (systemBundleInfo == null) { |
| // TODO Log |
| //throw new IllegalStateException("There is no systemBundle.\n"); |
| return; |
| } |
| if (state.isFullySupported()) |
| if (!this.checkResolve(systemBundleInfo, state)) { |
| printoutUnsatisfiedConstraints(systemBundleInfo, state); |
| return; |
| } |
| info.systemBundleLocation = systemBundleInfo.getLocation(); |
| BundleInfo[] fragments = state.getSystemFragmentedBundles(); |
| info.systemFragmentedBundleLocations = new URI[fragments.length]; |
| for (int i = 0; i < fragments.length; i++) |
| info.systemFragmentedBundleLocations[i] = fragments[i].getLocation(); |
| } |
| |
| public void updateBundles(Manipulator manipulator) throws IOException { |
| if (DEBUG) |
| System.out.println("SimpleConfiguratorManipulatorImpl#updateBundles()"); //$NON-NLS-1$ |
| |
| BundlesState bundleState = manipulator.getBundlesState(); |
| |
| if (bundleState == null) |
| return; |
| if (bundleState.isFullySupported()) |
| bundleState.resolve(true); |
| |
| BundleInfo[] currentBInfos = bundleState.getExpectedState(); |
| if (!isTargetConfiguratorBundle(currentBInfos)) |
| return; |
| Properties properties = new Properties(); |
| String[] jvmArgs = manipulator.getLauncherData().getJvmArgs(); |
| for (int i = 0; i < jvmArgs.length; i++) { |
| if (jvmArgs[i].startsWith("-D")) { //$NON-NLS-1$ |
| int index = jvmArgs[i].indexOf("="); //$NON-NLS-1$ |
| if (index > 0 && jvmArgs[i].length() > 2) { |
| String key = jvmArgs[i].substring(2, index); |
| String value = jvmArgs[i].substring(index + 1); |
| properties.setProperty(key, value); |
| } |
| } |
| } |
| |
| Utils.appendProperties(properties, manipulator.getConfigData().getProperties()); |
| boolean exclusiveInstallation = Boolean.valueOf(properties.getProperty(SimpleConfiguratorManipulatorImpl.PROP_KEY_EXCLUSIVE_INSTALLATION)).booleanValue(); |
| File configFile = getConfigFile(manipulator); |
| |
| BundleInfo[] toInstall = loadConfiguration(configFile.toURL(), ParserUtils.getOSGiInstallArea(Arrays.asList(manipulator.getLauncherData().getProgramArgs()), manipulator.getConfigData().getProperties(), manipulator.getLauncherData())); |
| |
| List toUninstall = new LinkedList(); |
| if (exclusiveInstallation) |
| for (int i = 0; i < currentBInfos.length; i++) { |
| boolean install = false; |
| for (int j = 0; j < toInstall.length; j++) |
| if (currentBInfos[i].getLocation().equals(toInstall[j].getLocation())) { |
| install = true; |
| break; |
| } |
| if (!install) |
| toUninstall.add(currentBInfos[i]); |
| } |
| |
| for (int i = 0; i < toInstall.length; i++) { |
| try { |
| bundleState.installBundle(toInstall[i]); |
| } catch (RuntimeException e) { |
| //Ignore |
| } |
| } |
| if (exclusiveInstallation) |
| for (Iterator ite = toUninstall.iterator(); ite.hasNext();) { |
| BundleInfo bInfo = (BundleInfo) ite.next(); |
| bundleState.uninstallBundle(bInfo); |
| } |
| |
| bundleState.resolve(true); |
| manipulator.getConfigData().setBundles(bundleState.getExpectedState()); |
| } |
| |
| public void cleanup(Manipulator manipulator) { |
| File outputFile = getConfigFile(manipulator); |
| outputFile.delete(); |
| |
| if (outputFile.getParentFile().isDirectory()) |
| outputFile.getParentFile().delete(); |
| } |
| } |