| /******************************************************************************* |
| * Copyright (c) 2007, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Ericsson AB (Pascal Rapicault) - Bug 397216 -[Shared] Better shared |
| * configuration change discovery |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.frameworkadmin.equinox; |
| |
| import java.io.*; |
| import java.net.*; |
| import java.util.*; |
| import org.eclipse.core.runtime.URIUtil; |
| import org.eclipse.equinox.frameworkadmin.BundleInfo; |
| import org.eclipse.equinox.internal.frameworkadmin.equinox.utils.FileUtils; |
| import org.eclipse.equinox.internal.frameworkadmin.utils.Utils; |
| import org.eclipse.equinox.internal.provisional.frameworkadmin.*; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.service.log.LogService; |
| |
| public class EquinoxFwConfigFileParser { |
| private static final Set<String> KNOWN_PROPERTIES = new HashSet<>(Arrays.asList(new String[] {EquinoxConstants.PROP_BUNDLES, EquinoxConstants.PROP_FW_EXTENSIONS, EquinoxConstants.PROP_INITIAL_STARTLEVEL, EquinoxConstants.PROP_BUNDLES_STARTLEVEL})); |
| private static final String CONFIG_DIR = "@config.dir/"; //$NON-NLS-1$ |
| private static final String KEY_ECLIPSE_PROV_DATA_AREA = "eclipse.p2.data.area"; //$NON-NLS-1$ |
| private static final String KEY_ORG_ECLIPSE_EQUINOX_SIMPLECONFIGURATOR_CONFIGURL = "org.eclipse.equinox.simpleconfigurator.configUrl"; //$NON-NLS-1$ |
| private static final String REFERENCE_SCHEME = "reference:"; //$NON-NLS-1$ |
| private static final String FILE_PROTOCOL = "file:"; //$NON-NLS-1$ |
| private static final String BASE_TIMESTAMP_FILE_CONFIGINI = ".baseConfigIniTimestamp"; //$NON-NLS-1$ |
| private static final String KEY_CONFIGINI_TIMESTAMP = "configIniTimestamp"; //$NON-NLS-1$ |
| |
| private static boolean DEBUG = false; |
| |
| public EquinoxFwConfigFileParser(BundleContext context) { |
| //Empty |
| } |
| |
| private static StringBuffer toOSGiBundleListForm(BundleInfo bundleInfo, URI location) { |
| StringBuffer locationString = new StringBuffer(REFERENCE_SCHEME); |
| if (URIUtil.isFileURI(location)) |
| locationString.append(URIUtil.toUnencodedString(location)); |
| else if (location.getScheme() == null) |
| locationString.append(FILE_PROTOCOL).append(URIUtil.toUnencodedString(location)); |
| else |
| locationString = new StringBuffer(URIUtil.toUnencodedString(location)); |
| |
| int startLevel = bundleInfo.getStartLevel(); |
| boolean toBeStarted = bundleInfo.isMarkedAsStarted(); |
| |
| StringBuffer sb = new StringBuffer(); |
| sb.append(locationString); |
| if (startLevel == BundleInfo.NO_LEVEL && !toBeStarted) |
| return sb; |
| sb.append('@'); |
| if (startLevel != BundleInfo.NO_LEVEL) |
| sb.append(startLevel); |
| if (toBeStarted) |
| sb.append(":start"); //$NON-NLS-1$ |
| return sb; |
| } |
| |
| private static boolean getMarkedAsStartedFormat(String startInfo) { |
| if (startInfo == null) |
| return false; |
| startInfo = startInfo.trim(); |
| int colon = startInfo.indexOf(':'); |
| if (colon > -1) { |
| return startInfo.substring(colon + 1).equals("start"); //$NON-NLS-1$ |
| } |
| return startInfo.equals("start"); //$NON-NLS-1$ |
| } |
| |
| private static int getStartLevel(String startInfo) { |
| if (startInfo == null) |
| return BundleInfo.NO_LEVEL; |
| startInfo = startInfo.trim(); |
| int colon = startInfo.indexOf(":"); //$NON-NLS-1$ |
| if (colon > 0) { |
| try { |
| return Integer.parseInt(startInfo.substring(0, colon)); |
| } catch (NumberFormatException e) { |
| return BundleInfo.NO_LEVEL; |
| } |
| } |
| return BundleInfo.NO_LEVEL; |
| } |
| |
| private void readBundlesList(Manipulator manipulator, URI osgiInstallArea, Properties props) throws NumberFormatException { |
| ConfigData configData = manipulator.getConfigData(); |
| |
| BundleInfo[] fwExtensions = parseBundleList(manipulator, props.getProperty(EquinoxConstants.PROP_FW_EXTENSIONS)); |
| if (fwExtensions != null) { |
| for (BundleInfo fwExtension : fwExtensions) { |
| fwExtension.setFragmentHost(Constants.SYSTEM_BUNDLE_SYMBOLICNAME); |
| configData.addBundle(fwExtension); |
| } |
| } |
| |
| BundleInfo[] bundles = parseBundleList(manipulator, props.getProperty(EquinoxConstants.PROP_BUNDLES)); |
| if (bundles != null) { |
| for (BundleInfo bundle : bundles) { |
| configData.addBundle(bundle); |
| } |
| } |
| } |
| |
| private BundleInfo[] parseBundleList(Manipulator manipulator, String value) { |
| if (value == null || value.length() == 0) |
| return null; |
| |
| List<BundleInfo> bundles = new ArrayList<>(); |
| String[] bInfoStrings = Utils.getTokens(value, ","); //$NON-NLS-1$ |
| for (String bInfoString : bInfoStrings) { |
| String entry = bInfoString.trim(); |
| entry = FileUtils.removeEquinoxSpecificProtocols(entry); |
| int indexStartInfo = entry.indexOf('@'); |
| String location = (indexStartInfo == -1) ? entry : entry.substring(0, indexStartInfo); |
| URI realLocation = null; |
| if (manipulator.getLauncherData().getFwJar() != null) { |
| File parentFile = manipulator.getLauncherData().getFwJar().getParentFile(); |
| try { |
| realLocation = URIUtil.makeAbsolute(FileUtils.fromFileURL(location), parentFile.toURI()); |
| } catch (URISyntaxException e) { |
| // try searching as a simple location |
| realLocation = FileUtils.getEclipsePluginFullLocation(location, parentFile); |
| } |
| } |
| String slAndFlag = (indexStartInfo > -1) ? entry.substring(indexStartInfo + 1) : null; |
| boolean markedAsStarted = getMarkedAsStartedFormat(slAndFlag); |
| int startLevel = getStartLevel(slAndFlag); |
| if (realLocation != null) { |
| bundles.add(new BundleInfo(realLocation, startLevel, markedAsStarted)); |
| continue; |
| } |
| if (location != null && location.startsWith(FILE_PROTOCOL)) |
| try { |
| bundles.add(new BundleInfo(FileUtils.fromFileURL(location), startLevel, markedAsStarted)); |
| continue; |
| } catch (URISyntaxException e) { |
| //Ignore |
| } |
| //Fallback case, we use the location as a string |
| bundles.add(new BundleInfo(location, null, null, startLevel, markedAsStarted)); |
| } |
| return bundles.toArray(new BundleInfo[bundles.size()]); |
| } |
| |
| private void writeBundlesList(File fwJar, Properties props, BundleInfo[] bundles) { |
| StringBuilder osgiBundlesList = new StringBuilder(); |
| StringBuilder osgiFrameworkExtensionsList = new StringBuilder(); |
| for (BundleInfo bundle : bundles) { |
| //framework jar does not get stored on the bundle list, figure out who that is. |
| if (fwJar != null) { |
| if (URIUtil.sameURI(fwJar.toURI(), bundle.getLocation())) |
| continue; |
| } else if (EquinoxConstants.FW_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) |
| continue; |
| |
| URI location = fwJar != null ? URIUtil.makeRelative(bundle.getLocation(), fwJar.getParentFile().toURI()) : bundle.getLocation(); |
| |
| String fragmentHost = bundle.getFragmentHost(); |
| boolean isFrameworkExtension = fragmentHost != null && (fragmentHost.startsWith(EquinoxConstants.FW_SYMBOLIC_NAME) || fragmentHost.startsWith(Constants.SYSTEM_BUNDLE_SYMBOLICNAME)); |
| |
| if (isFrameworkExtension) { |
| bundle.setStartLevel(BundleInfo.NO_LEVEL); |
| bundle.setMarkedAsStarted(false); |
| osgiFrameworkExtensionsList.append(toOSGiBundleListForm(bundle, location)); |
| osgiFrameworkExtensionsList.append(','); |
| } else { |
| osgiBundlesList.append(toOSGiBundleListForm(bundle, location)); |
| osgiBundlesList.append(','); |
| } |
| } |
| if (osgiFrameworkExtensionsList.length() > 0) |
| osgiFrameworkExtensionsList.deleteCharAt(osgiFrameworkExtensionsList.length() - 1); |
| props.setProperty(EquinoxConstants.PROP_FW_EXTENSIONS, osgiFrameworkExtensionsList.toString()); |
| |
| if (osgiBundlesList.length() > 0) |
| osgiBundlesList.deleteCharAt(osgiBundlesList.length() - 1); |
| props.setProperty(EquinoxConstants.PROP_BUNDLES, osgiBundlesList.toString()); |
| } |
| |
| /** |
| * inputFile must be not a directory but a file. |
| * |
| * @param manipulator |
| * @param inputFile |
| * @throws IOException |
| */ |
| public void readFwConfig(Manipulator manipulator, File inputFile) throws IOException, URISyntaxException { |
| if (inputFile.isDirectory()) |
| throw new IllegalArgumentException(NLS.bind(Messages.exception_inputFileIsDirectory, inputFile)); |
| |
| boolean baseHasChanged = false; |
| //Initialize data structures |
| ConfigData configData = manipulator.getConfigData(); |
| LauncherData launcherData = manipulator.getLauncherData(); |
| configData.initialize(); |
| configData.setBundles(null); |
| |
| // load configuration properties |
| Properties props = loadProperties(inputFile); |
| |
| // load shared configuration properties |
| Properties sharedConfigProperties = getSharedConfiguration(ParserUtils.getOSGiInstallArea(Arrays.asList(manipulator.getLauncherData().getProgramArgs()), props, manipulator.getLauncherData()), props.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA)); |
| if (sharedConfigProperties != null) { |
| baseHasChanged = hasBaseChanged(inputFile, manipulator, props); |
| if (!baseHasChanged) |
| sharedConfigProperties.putAll(props); |
| else { |
| sharedConfigProperties.put(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA, props.get(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA)); |
| } |
| |
| props = sharedConfigProperties; |
| } |
| |
| //Start figuring out stuffs |
| //URI rootURI = launcherData.getLauncher() != null ? launcherData.getLauncher().getParentFile().toURI() : null; |
| |
| readFwJarLocation(configData, launcherData, props); |
| URI configArea = inputFile.getParentFile().toURI(); |
| //readLauncherPath(props, rootURI); |
| readp2DataArea(props, configArea); |
| readSimpleConfiguratorURL(props, configArea); |
| |
| if (!baseHasChanged) |
| readBundlesList(manipulator, ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), props, launcherData).toURI(), props); |
| readInitialStartLeve(configData, props); |
| readDefaultStartLevel(configData, props); |
| |
| for (Enumeration<Object> enumeration = props.keys(); enumeration.hasMoreElements();) { |
| String key = (String) enumeration.nextElement(); |
| if (KNOWN_PROPERTIES.contains(key)) |
| continue; |
| String value = props.getProperty(key); |
| configData.setProperty(key, value); |
| } |
| Log.log(LogService.LOG_INFO, NLS.bind(Messages.log_configFile, inputFile.getAbsolutePath())); |
| } |
| |
| private boolean hasBaseChanged(File configIni, Manipulator manipulator, Properties configProps) { |
| LauncherData launcherData = manipulator.getLauncherData(); |
| File sharedConfigIni = findSharedConfigIniFile(ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), configProps, launcherData), configProps.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA)); |
| File timestampFile = new File(configIni.getParentFile(), BASE_TIMESTAMP_FILE_CONFIGINI); |
| Properties timestamps; |
| try { |
| timestamps = loadProperties(timestampFile); |
| } catch (FileNotFoundException e) { |
| return false; |
| } catch (IOException e) { |
| return false; |
| } |
| return !String.valueOf(sharedConfigIni.lastModified()).equals(timestamps.getProperty(KEY_CONFIGINI_TIMESTAMP)); |
| } |
| |
| private void readDefaultStartLevel(ConfigData configData, Properties props) { |
| if (props.getProperty(EquinoxConstants.PROP_BUNDLES_STARTLEVEL) != null) |
| configData.setInitialBundleStartLevel(Integer.parseInt(props.getProperty(EquinoxConstants.PROP_BUNDLES_STARTLEVEL))); |
| } |
| |
| private void writeDefaultStartLevel(ConfigData configData, Properties props) { |
| if (configData.getInitialBundleStartLevel() != BundleInfo.NO_LEVEL) |
| props.setProperty(EquinoxConstants.PROP_BUNDLES_STARTLEVEL, Integer.toString(configData.getInitialBundleStartLevel())); |
| } |
| |
| private void readInitialStartLeve(ConfigData configData, Properties props) { |
| if (props.getProperty(EquinoxConstants.PROP_INITIAL_STARTLEVEL) != null) |
| configData.setBeginningFwStartLevel(Integer.parseInt(props.getProperty(EquinoxConstants.PROP_INITIAL_STARTLEVEL))); |
| } |
| |
| private void writeInitialStartLevel(ConfigData configData, Properties props) { |
| if (configData.getBeginingFwStartLevel() != BundleInfo.NO_LEVEL) |
| props.setProperty(EquinoxConstants.PROP_INITIAL_STARTLEVEL, Integer.toString(configData.getBeginingFwStartLevel())); |
| } |
| |
| private File readFwJarLocation(ConfigData configData, LauncherData launcherData, Properties props) throws URISyntaxException { |
| File fwJar = null; |
| if (props.getProperty(EquinoxConstants.PROP_OSGI_FW) != null) { |
| URI absoluteFwJar = null; |
| absoluteFwJar = URIUtil.makeAbsolute(FileUtils.fromFileURL(props.getProperty(EquinoxConstants.PROP_OSGI_FW)), ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), props, launcherData).toURI()); |
| |
| props.setProperty(EquinoxConstants.PROP_OSGI_FW, absoluteFwJar.toString()); |
| String fwJarString = props.getProperty(EquinoxConstants.PROP_OSGI_FW); |
| if (fwJarString != null) { |
| fwJar = URIUtil.toFile(absoluteFwJar); |
| if (fwJar == null) |
| throw new IllegalStateException(Messages.exception_noFrameworkLocation); |
| //Here we overwrite the value read from eclipse.ini, because the value of osgi.framework always takes precedence. |
| launcherData.setFwJar(fwJar); |
| } else { |
| throw new IllegalStateException(Messages.exception_noFrameworkLocation); |
| } |
| } |
| if (launcherData.getFwJar() != null) |
| configData.addBundle(new BundleInfo(launcherData.getFwJar().toURI())); |
| return launcherData.getFwJar(); |
| } |
| |
| private void writeFwJarLocation(ConfigData configData, LauncherData launcherData, Properties props) { |
| if (launcherData.getFwJar() == null) |
| return; |
| props.setProperty(EquinoxConstants.PROP_OSGI_FW, FileUtils.toFileURL(URIUtil.makeRelative(launcherData.getFwJar().toURI(), ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), configData.getProperties(), launcherData).toURI()))); |
| } |
| |
| private static Properties loadProperties(File inputFile) throws FileNotFoundException, IOException { |
| Properties props = new Properties(); |
| InputStream is = null; |
| try { |
| is = new FileInputStream(inputFile); |
| props.load(is); |
| } finally { |
| try { |
| if (is != null) |
| is.close(); |
| } catch (IOException e) { |
| Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_failed_reading_properties, inputFile)); |
| } |
| is = null; |
| } |
| return props; |
| } |
| |
| private File findSharedConfigIniFile(File base, String sharedConfigurationArea) { |
| if (base == null) |
| return null; |
| URL rootURL; |
| try { |
| rootURL = base.toURL(); |
| } catch (MalformedURLException e1) { |
| return null; |
| } |
| if (rootURL == null) |
| return null; |
| |
| URL sharedConfigurationURL = null; |
| try { |
| sharedConfigurationURL = new URL(sharedConfigurationArea); |
| } catch (MalformedURLException e) { |
| // unexpected since this was written by the framework |
| Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_shared_config_url, sharedConfigurationArea)); |
| return null; |
| } |
| |
| // check for a relative URL |
| if (!sharedConfigurationURL.getPath().startsWith("/")) { //$NON-NLS-1$ |
| if (!rootURL.getProtocol().equals(sharedConfigurationURL.getProtocol())) { |
| Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_shared_config_relative_url, rootURL.toExternalForm(), sharedConfigurationArea)); |
| return null; |
| } |
| try { |
| sharedConfigurationURL = new URL(rootURL, sharedConfigurationArea); |
| } catch (MalformedURLException e) { |
| // unexpected since this was written by the framework |
| Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_shared_config_relative_url, rootURL.toExternalForm(), sharedConfigurationArea)); |
| return null; |
| } |
| } |
| File sharedConfigurationFolder = EquinoxManipulatorImpl.toFile(sharedConfigurationURL); |
| if (!sharedConfigurationFolder.isDirectory()) { |
| Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_shared_config_file_missing, sharedConfigurationFolder)); |
| return null; |
| } |
| |
| File sharedConfigIni = new File(sharedConfigurationFolder, EquinoxConstants.CONFIG_INI); |
| if (!sharedConfigIni.exists()) { |
| Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_shared_config_file_missing, sharedConfigIni)); |
| return null; |
| } |
| |
| return sharedConfigIni; |
| } |
| |
| private void readp2DataArea(Properties props, URI configArea) throws URISyntaxException { |
| if (props.getProperty(KEY_ECLIPSE_PROV_DATA_AREA) != null) { |
| String url = props.getProperty(KEY_ECLIPSE_PROV_DATA_AREA); |
| if (url != null) { |
| if (url.startsWith(CONFIG_DIR)) |
| url = "file:" + url.substring(CONFIG_DIR.length()); //$NON-NLS-1$ |
| props.setProperty(KEY_ECLIPSE_PROV_DATA_AREA, URIUtil.makeAbsolute(FileUtils.fromFileURL(url), configArea).toString()); |
| } |
| } |
| } |
| |
| private void writep2DataArea(ConfigData configData, Properties props, URI configArea) throws URISyntaxException { |
| String dataArea = getFwProperty(configData, KEY_ECLIPSE_PROV_DATA_AREA); |
| if (dataArea != null) { |
| if (dataArea.startsWith(CONFIG_DIR)) { |
| props.setProperty(KEY_ECLIPSE_PROV_DATA_AREA, dataArea); |
| return; |
| } |
| URI dataAreaURI = new URI(dataArea); |
| URI relative = URIUtil.makeRelative(dataAreaURI, configArea); |
| if (dataAreaURI.equals(relative)) { |
| props.setProperty(KEY_ECLIPSE_PROV_DATA_AREA, FileUtils.toFileURL(dataAreaURI)); |
| return; |
| } |
| String result = URIUtil.toUnencodedString(relative); |
| //We only relativize up to the level where the p2 and config folder are siblings (e.g. foo/p2 and foo/config) |
| if (result.startsWith("../..")) //$NON-NLS-1$ |
| result = dataArea; |
| else if (!result.equals(dataArea)) { |
| props.setProperty(KEY_ECLIPSE_PROV_DATA_AREA, CONFIG_DIR + result); |
| return; |
| } |
| props.setProperty(KEY_ECLIPSE_PROV_DATA_AREA, FileUtils.toFileURL(new URI(result))); |
| } |
| } |
| |
| private void readSimpleConfiguratorURL(Properties props, URI configArea) throws URISyntaxException { |
| if (props.getProperty(KEY_ORG_ECLIPSE_EQUINOX_SIMPLECONFIGURATOR_CONFIGURL) != null) |
| props.setProperty(KEY_ORG_ECLIPSE_EQUINOX_SIMPLECONFIGURATOR_CONFIGURL, URIUtil.makeAbsolute(FileUtils.fromFileURL(props.getProperty(KEY_ORG_ECLIPSE_EQUINOX_SIMPLECONFIGURATOR_CONFIGURL)), configArea).toString()); |
| } |
| |
| private void writeSimpleConfiguratorURL(ConfigData configData, Properties props, URI configArea) throws URISyntaxException { |
| //FIXME How would someone set such a value..... |
| String value = getFwProperty(configData, KEY_ORG_ECLIPSE_EQUINOX_SIMPLECONFIGURATOR_CONFIGURL); |
| if (value != null) |
| props.setProperty(KEY_ORG_ECLIPSE_EQUINOX_SIMPLECONFIGURATOR_CONFIGURL, FileUtils.toFileURL(URIUtil.makeRelative(URIUtil.fromString(value), configArea))); |
| } |
| |
| private String getFwProperty(ConfigData data, String key) { |
| return data.getProperty(key); |
| } |
| |
| public void saveFwConfig(BundleInfo[] bInfos, Manipulator manipulator, boolean backup, boolean relative) throws IOException {//{ |
| ConfigData configData = manipulator.getConfigData(); |
| LauncherData launcherData = manipulator.getLauncherData(); |
| //Get the OSGi jar from the bundle.info |
| File fwJar = EquinoxBundlesState.getSystemBundleFromBundleInfos(configData); |
| launcherData.setFwJar(fwJar); |
| |
| File outputFile = launcherData.getFwConfigLocation(); |
| if (outputFile.exists()) { |
| if (outputFile.isFile()) { |
| if (!outputFile.getName().equals(EquinoxConstants.CONFIG_INI)) |
| throw new IllegalStateException(NLS.bind(Messages.exception_fwConfigLocationName, outputFile.getAbsolutePath(), EquinoxConstants.CONFIG_INI)); |
| } else { // Directory |
| outputFile = new File(outputFile, EquinoxConstants.CONFIG_INI); |
| } |
| } else { |
| if (!outputFile.getName().equals(EquinoxConstants.CONFIG_INI)) { |
| if (!outputFile.mkdir()) |
| throw new IOException(NLS.bind(Messages.exception_failedToCreateDir, outputFile)); |
| outputFile = new File(outputFile, EquinoxConstants.CONFIG_INI); |
| } |
| } |
| |
| Properties configProps = new Properties(); |
| //URI rootURI = launcherData.getLauncher() != null ? launcherData.getLauncher().getParentFile().toURI() : null; |
| |
| writeFwJarLocation(configData, launcherData, configProps); |
| try { |
| //writeLauncherPath(configData, configProps, rootURI); |
| URI configArea = manipulator.getLauncherData().getFwConfigLocation().toURI(); |
| writep2DataArea(configData, configProps, configArea); |
| writeSimpleConfiguratorURL(configData, configProps, configArea); |
| writeBundlesList(launcherData.getFwJar(), configProps, bInfos); |
| writeInitialStartLevel(configData, configProps); |
| writeDefaultStartLevel(configData, configProps); |
| } catch (URISyntaxException e) { |
| throw new FrameworkAdminRuntimeException(e, Messages.exception_errorSavingConfigIni); |
| } |
| |
| Properties original = configData.getProperties(); |
| original.putAll(configProps); |
| configProps = original; |
| |
| //Deal with the fw jar and ensure it is not set. |
| //TODO This can't be done before because of the previous calls to appendProperties |
| |
| if (configProps == null || configProps.size() == 0) { |
| Log.log(LogService.LOG_WARNING, this, "saveFwConfig() ", Messages.log_configProps); //$NON-NLS-1$ |
| return; |
| } |
| if (!Utils.createParentDir(outputFile)) { |
| throw new IllegalStateException(NLS.bind(Messages.exception_failedToCreateDir, outputFile.getParent())); |
| } |
| |
| if (DEBUG) |
| Utils.printoutProperties(System.out, "configProps", configProps); //$NON-NLS-1$ |
| |
| if (backup) |
| if (outputFile.exists()) { |
| File dest = Utils.getSimpleDataFormattedFile(outputFile); |
| if (!outputFile.renameTo(dest)) |
| throw new IOException(NLS.bind(Messages.exception_failedToRename, outputFile, dest)); |
| Log.log(LogService.LOG_INFO, this, "saveFwConfig()", NLS.bind(Messages.log_renameSuccessful, outputFile, dest)); //$NON-NLS-1$ |
| } |
| |
| filterPropertiesFromSharedArea(configProps, manipulator); |
| saveProperties(outputFile, configProps); |
| rememberSharedConfigurationTimestamp(configProps, manipulator, outputFile.getParentFile()); |
| } |
| |
| private void saveProperties(File outputFile, Properties configProps) throws IOException { |
| String header = "This configuration file was written by: " + this.getClass().getName(); //$NON-NLS-1$ |
| FileOutputStream out = null; |
| try { |
| out = new FileOutputStream(outputFile); |
| configProps.store(out, header); |
| Log.log(LogService.LOG_INFO, NLS.bind(Messages.log_propertiesSaved, outputFile)); |
| } finally { |
| try { |
| out.flush(); |
| out.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private void filterPropertiesFromSharedArea(Properties configProps, Manipulator manipulator) { |
| LauncherData launcherData = manipulator.getLauncherData(); |
| //Remove from the config file that we are about to write the properties that are unchanged compared to what is in the base |
| Properties sharedConfigProperties = getSharedConfiguration(ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), configProps, launcherData), configProps.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA)); |
| if (sharedConfigProperties == null) |
| return; |
| Enumeration<?> keys = configProps.propertyNames(); |
| while (keys.hasMoreElements()) { |
| String key = (String) keys.nextElement(); |
| String sharedValue = sharedConfigProperties.getProperty(key); |
| if (sharedValue == null) |
| continue; |
| String value = configProps.getProperty(key); |
| if (equalsIgnoringSeparators(sharedValue, value)) { |
| configProps.remove(key); |
| continue; |
| } |
| |
| // never write out the osgi.framework property in the user's local config.ini |
| // See https://bugs.eclipse.org/329583 |
| if (key.equals(EquinoxConstants.PROP_OSGI_FW)) { |
| configProps.remove(key); |
| continue; |
| } |
| |
| if (key.equals(EquinoxConstants.PROP_BUNDLES) && equalBundleLists(manipulator, value, sharedValue)) { |
| configProps.remove(key); |
| continue; |
| } |
| |
| if (key.equals(EquinoxConstants.PROP_FW_EXTENSIONS) && equalBundleLists(manipulator, value, sharedValue)) { |
| configProps.remove(key); |
| continue; |
| } |
| } |
| } |
| |
| private boolean equalBundleLists(Manipulator manipulator, String value, String sharedValue) { |
| BundleInfo[] bundles = parseBundleList(manipulator, value); |
| BundleInfo[] sharedBundles = parseBundleList(manipulator, sharedValue); |
| if (bundles == null || sharedBundles == null || bundles.length != sharedBundles.length) |
| return false; |
| |
| List<BundleInfo> compareList = new ArrayList<>(Arrays.asList(bundles)); |
| compareList.removeAll(Arrays.asList(sharedBundles)); |
| return compareList.isEmpty(); |
| } |
| |
| private boolean equalsIgnoringSeparators(String s1, String s2) { |
| if (s1 == null && s2 == null) |
| return true; |
| if (s1 == null || s2 == null) |
| return false; |
| StringBuffer sb1 = new StringBuffer(s1); |
| StringBuffer sb2 = new StringBuffer(s2); |
| canonicalizePathsForComparison(sb1); |
| canonicalizePathsForComparison(sb2); |
| return sb1.toString().equals(sb2.toString()); |
| } |
| |
| private void canonicalizePathsForComparison(StringBuffer s) { |
| final String[] tokens = new String[] {"\\\\", "\\", "//", "/"}; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ |
| for (String token : tokens) { |
| int idx = s.length(); |
| for (int i = s.length(); i != 0 && idx != -1; i--) { |
| idx = s.toString().lastIndexOf(token, idx); |
| if (idx != -1) { |
| s.replace(idx, idx + token.length(), "^"); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| |
| private void rememberSharedConfigurationTimestamp(Properties configProps, Manipulator manipulator, File folder) throws IOException { |
| LauncherData launcherData = manipulator.getLauncherData(); |
| //Remove from the config file that we are about to write the properties that are unchanged compared to what is in the base |
| File sharedConfigIni = findSharedConfigIniFile(ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), configProps, launcherData), configProps.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA)); |
| if (sharedConfigIni == null) |
| return; |
| File timestampFile = new File(folder, BASE_TIMESTAMP_FILE_CONFIGINI); |
| Properties timestamps; |
| try { |
| timestamps = loadProperties(timestampFile); |
| } catch (IOException e) { |
| timestamps = new Properties(); |
| } |
| timestamps.setProperty(KEY_CONFIGINI_TIMESTAMP, String.valueOf(sharedConfigIni.lastModified())); |
| saveProperties(timestampFile, timestamps); |
| } |
| |
| private Properties getSharedConfiguration(File baseFile, String sharedConfigurationArea) { |
| if (sharedConfigurationArea == null) |
| return null; |
| File sharedConfigIni = findSharedConfigIniFile(baseFile, sharedConfigurationArea); |
| if (sharedConfigIni != null && sharedConfigIni.exists()) { |
| try { |
| return loadProperties(sharedConfigIni); |
| } catch (FileNotFoundException e) { |
| return null; |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| return null; |
| } |
| } |