diff options
author | Pascal Rapicault (Ericsson) | 2013-01-28 04:41:19 +0000 |
---|---|---|
committer | Pascal Rapicault | 2013-01-28 05:08:43 +0000 |
commit | b86b21139a5367412cd51e86d359af33b0c03c3c (patch) | |
tree | 1bc01c47dfdcedf7ebc9888aa52002e1d9c809a2 | |
parent | c847dee6f33950f48ecd1d8eca18729f6ffc470f (diff) | |
download | rt.equinox.p2-b86b21139a5367412cd51e86d359af33b0c03c3c.tar.gz rt.equinox.p2-b86b21139a5367412cd51e86d359af33b0c03c3c.tar.xz rt.equinox.p2-b86b21139a5367412cd51e86d359af33b0c03c3c.zip |
Squashed commit of the following:v20130128-050843
304132 - [shared] upgrading shared base causes loss of user installer
plug-ins
commit 8461eae803454c36f6a18013c44e37c20810d141
Merge: 23bc40c c847dee
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Sun Jan 27 22:11:45 2013 -0500
Merge branch 'master' into prapicau/sharedInstall-part1
commit 23bc40c4f85b01f37fb0ac23992524a7c8ccd892
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Sun Jan 27 21:38:07 2013 -0500
Use constant and fix copyright header
commit bc11d565b32b5b5c8bb5c93e5ea666b1d06b43ff
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Sun Jan 27 21:33:32 2013 -0500
Annotate new profile with a base profile timestamp
commit ba4e7abda6e44ad47aead2fade320f9be08b27d5
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Sun Jan 27 21:32:20 2013 -0500
Add constants
commit 54c8d356527d1321e25bd837e674b758aa61e248
Author: Krzysztof Daniel <kdaniel@redhat.com>
Date: Thu Jan 24 13:23:15 2013 +0100
SimpleConfiguratorImpl refactoring and tests.
commit 6177c466083fb7676ebfe5790bc0951043b3b727
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Thu Jan 24 11:59:45 2013 -0500
detection of change in the reconciler
commit f04f46d6d283a6235367872b5eaa7e3e483509ed
Merge: 5243d49 c32f111
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Thu Jan 24 11:43:01 2013 -0500
Merge branch 'master' into prapicau/sharedInstall-part1
commit 5243d497142b0f2631bcda8b5fed67d7ce0ddbd5
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Wed Jan 23 15:26:41 2013 -0500
always use shared install when the ignoreUserConfig property is set
commit 12eccdad23420e7ba91f99221c852f7e32eb11de
Merge: c68f394 1e9fc23
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Wed Jan 23 10:29:56 2013 -0500
Merge branch 'master' into prapicau/sharedInstall-part1
commit c68f394a39c4215ed7ac960495a3c59eb3cf3eee
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Tue Jan 22 22:24:04 2013 -0500
Bug 398853 - [UI] installed IU shows as update
commit aa6d632dea7ff54e9ba7c3c7f9f2ecd30afe5906
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Tue Jan 22 13:50:05 2013 -0500
Differentiate initial profile creation from reset.
commit 4fb3429ed3fd673d5daf7ca1ce9bf1184a0afcb3
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Tue Jan 22 13:49:23 2013 -0500
Only show migration dialog once
commit a34eac29e330a41991d003e2dcd2d8b264d77145
Author: Krzysztof Daniel <kdaniel@redhat.com>
Date: Mon Jan 21 12:40:36 2013 +0100
397216: [Shared] Better shared configuration change discovery
Perform dropins reindexing after master configuration had been changed.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=397216
commit 6981ca8bd3c1db9ab2dd9484a1f7c8950fcd1abe
Merge: dfec14b 33ca260
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Mon Jan 21 15:04:28 2013 -0500
Merge branch 'master' into prapicau/sharedInstall-part1
commit dfec14b67a69b6fa14efb860ef8a986053a58917
Merge: 6067d05 ba5f048
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Mon Jan 21 11:34:38 2013 -0500
Merge branch 'master' into prapicau/sharedInstall-part1
commit ba5f048a3ac5563430104adeb173dc4f7dda6eca
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Mon Jan 21 11:28:20 2013 -0500
Bug 398539 - Got a NPE trying to add a new update site
commit 6067d05e402fe796debd9d25604cae7026df5b48
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Fri Jan 18 16:16:18 2013 -0500
only write timestamp file for shared install
commit 8ab9410d8c797e6a8d2a97e4ba46da3d548440fa
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Fri Jan 18 13:56:46 2013 -0500
Cache the fact that the state.properties is missing
commit fefbc5bdec2ce6cdb7fd71b1790bf8abdee8737a
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Fri Jan 18 13:51:54 2013 -0500
Fix bug in change detection logic
commit 3de6f8593d5c1b21be3bd21000e34acb945056b1
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Wed Jan 16 19:50:32 2013 -0500
Detect changes in the bundles.info
commit 446649c95063dca689f4d2c886b26975a11f3499
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Tue Jan 15 14:04:57 2013 -0500
Simple notification to let the user know that the base has changed.
commit e004dc5270abd33a60da9a8d0340409bc8e4a38d
Author: Pascal Rapicault <pascal@rapicault.net>
Date: Fri Jan 11 14:15:20 2013 +0100
ignore user config.ini if out of sync with base
commit a0235c0c95b9047e64d6e24c9d8f6b8588087113
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Wed Dec 12 09:57:36 2012 -0500
Obtain specific profile state properties without locking
commit 1d0f129ca70716e7a181631e1ba0e71f16e35e08
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Mon Dec 10 16:06:38 2012 -0500
Remember the timestamp of the base config.ini
commit 3134caecfd244fd88386f3ce4bd1a6fc71fe8007
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Wed Dec 5 16:21:50 2012 -0500
Support to ignore the existing profile if the base has changed
commit 7f0ba4e643f40bc0143617b93b2238388d38216d
Author: Pascal Rapicault (Ericsson) <pascal.rapicault@ericsson.com>
Date: Tue Dec 4 11:23:37 2012 -0500
Remove externalization warning
19 files changed, 667 insertions, 112 deletions
diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxFwConfigFileParser.java b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxFwConfigFileParser.java index 7f19233a7..3f7e4ffd8 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxFwConfigFileParser.java +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxFwConfigFileParser.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,6 +7,8 @@ * * 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; @@ -30,6 +32,9 @@ public class EquinoxFwConfigFileParser { 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) { @@ -198,6 +203,7 @@ public class EquinoxFwConfigFileParser { 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(); @@ -208,9 +214,15 @@ public class EquinoxFwConfigFileParser { Properties props = loadProperties(inputFile); // load shared configuration properties - Properties sharedConfigProperties = getSharedConfiguration(props.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA), ParserUtils.getOSGiInstallArea(Arrays.asList(manipulator.getLauncherData().getProgramArgs()), props, manipulator.getLauncherData())); + Properties sharedConfigProperties = getSharedConfiguration(ParserUtils.getOSGiInstallArea(Arrays.asList(manipulator.getLauncherData().getProgramArgs()), props, manipulator.getLauncherData()), props.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA)); if (sharedConfigProperties != null) { - sharedConfigProperties.putAll(props); + 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; } @@ -222,7 +234,9 @@ public class EquinoxFwConfigFileParser { //readLauncherPath(props, rootURI); readp2DataArea(props, configArea); readSimpleConfiguratorURL(props, configArea); - readBundlesList(manipulator, ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), props, launcherData).toURI(), props); + + if (!baseHasChanged) + readBundlesList(manipulator, ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), props, launcherData).toURI(), props); readInitialStartLeve(configData, props); readDefaultStartLevel(configData, props); @@ -236,6 +250,21 @@ public class EquinoxFwConfigFileParser { 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))); @@ -293,7 +322,8 @@ public class EquinoxFwConfigFileParser { props.load(is); } finally { try { - is.close(); + if (is != null) + is.close(); } catch (IOException e) { Log.log(LogService.LOG_WARNING, NLS.bind(Messages.log_failed_reading_properties, inputFile)); } @@ -302,7 +332,18 @@ public class EquinoxFwConfigFileParser { return props; } - private File findSharedConfigIniFile(URL rootURL, String sharedConfigurationArea) { + 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); @@ -415,7 +456,6 @@ public class EquinoxFwConfigFileParser { outputFile = new File(outputFile, EquinoxConstants.CONFIG_INI); } } - String header = "This configuration file was written by: " + this.getClass().getName(); //$NON-NLS-1$ Properties configProps = new Properties(); //URI rootURI = launcherData.getLauncher() != null ? launcherData.getLauncher().getParentFile().toURI() : null; @@ -459,13 +499,18 @@ public class EquinoxFwConfigFileParser { 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 = makeRelative(configProps, launcherData.getLauncher().getParentFile().toURI(), fwJar, outputFile.getParentFile(), getOSGiInstallArea(manipulator.getLauncherData())); - filterPropertiesFromSharedArea(configProps, manipulator); configProps.store(out, header); - Log.log(LogService.LOG_INFO, NLS.bind(Messages.log_fwConfigSave, outputFile)); + Log.log(LogService.LOG_INFO, NLS.bind(Messages.log_propertiesSaved, outputFile)); } finally { try { out.flush(); @@ -473,14 +518,13 @@ public class EquinoxFwConfigFileParser { } catch (IOException e) { e.printStackTrace(); } - out = null; } } 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(configProps.getProperty(EquinoxConstants.PROP_SHARED_CONFIGURATION_AREA), ParserUtils.getOSGiInstallArea(Arrays.asList(launcherData.getProgramArgs()), configProps, launcherData)); + 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(); @@ -549,16 +593,28 @@ public class EquinoxFwConfigFileParser { } } - private Properties getSharedConfiguration(String sharedConfigurationArea, File baseFile) { - if (sharedConfigurationArea == null) - return null; - File sharedConfigIni; + 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 { - sharedConfigIni = findSharedConfigIniFile(baseFile.toURL(), sharedConfigurationArea); - } catch (MalformedURLException e) { - return null; + timestamps = loadProperties(timestampFile); + } catch (IOException e) { + timestamps = new Properties(); } - if (sharedConfigIni != null && sharedConfigIni.exists()) + 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) { @@ -566,6 +622,7 @@ public class EquinoxFwConfigFileParser { } catch (IOException e) { return null; } + } return null; } } diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/Messages.java b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/Messages.java index 2453f7ba3..66920c277 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/Messages.java +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/Messages.java @@ -31,7 +31,7 @@ public class Messages extends NLS { public static String log_configFile; public static String log_configProps; public static String log_renameSuccessful; - public static String log_fwConfigSave; + public static String log_propertiesSaved; public static String log_launcherConfigSave; public static String log_shared_config_url; public static String log_shared_config_relative_url; diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/messages.properties b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/messages.properties index a5d0d5083..3c84f4dcd 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/messages.properties +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/messages.properties @@ -26,7 +26,7 @@ exception_errorReadingFile = An error occured while reading {0}. log_configFile= Configuration file ({0}) has been read successfully. log_configProps= Configuration properties is empty. log_renameSuccessful= Successfully renamed {0} to {1}. -log_fwConfigSave= Framework Configuration was saved successfully in {0}. +log_propertiesSaved= File {0} was saved successfully. log_launcherConfigSave= Launcher Configuration was saved successfully in {0}. log_shared_config_url=Failed creating shared configuration url for {0}. log_shared_config_relative_url=Failed creating shared configuration url for root: {0} and sharedConfiguration: {1}. diff --git a/bundles/org.eclipse.equinox.frameworkadmin.test/dataFile/sharedconfiguration/ignoreUserConfig/config.ini b/bundles/org.eclipse.equinox.frameworkadmin.test/dataFile/sharedconfiguration/ignoreUserConfig/config.ini new file mode 100644 index 000000000..cb5fd8609 --- /dev/null +++ b/bundles/org.eclipse.equinox.frameworkadmin.test/dataFile/sharedconfiguration/ignoreUserConfig/config.ini @@ -0,0 +1 @@ +sharedKey=sharedValue
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.frameworkadmin.test/dataFile/sharedconfiguration/ignoreUserConfig/user-config.ini b/bundles/org.eclipse.equinox.frameworkadmin.test/dataFile/sharedconfiguration/ignoreUserConfig/user-config.ini new file mode 100644 index 000000000..8797ba532 --- /dev/null +++ b/bundles/org.eclipse.equinox.frameworkadmin.test/dataFile/sharedconfiguration/ignoreUserConfig/user-config.ini @@ -0,0 +1,3 @@ +osgi.sharedConfiguration.area=file\:configuration/ +userKey=userValue + diff --git a/bundles/org.eclipse.equinox.frameworkadmin.test/src/org/eclipse/equinox/frameworkadmin/tests/SharedConfigurationTest.java b/bundles/org.eclipse.equinox.frameworkadmin.test/src/org/eclipse/equinox/frameworkadmin/tests/SharedConfigurationTest.java index 134d7bec0..0d0eba503 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.test/src/org/eclipse/equinox/frameworkadmin/tests/SharedConfigurationTest.java +++ b/bundles/org.eclipse.equinox.frameworkadmin.test/src/org/eclipse/equinox/frameworkadmin/tests/SharedConfigurationTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2010 IBM Corporation and others. + * Copyright (c) 2008, 2013 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 @@ -7,20 +7,29 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ericsson AB (Pascal Rapicault) - Improve shared install *******************************************************************************/ package org.eclipse.equinox.frameworkadmin.tests; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.net.URISyntaxException; +import java.util.Properties; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.URIUtil; +import org.eclipse.equinox.frameworkadmin.BundleInfo; +import org.eclipse.equinox.internal.frameworkadmin.equinox.Log; +import org.eclipse.equinox.internal.frameworkadmin.equinox.Messages; import org.eclipse.equinox.internal.provisional.frameworkadmin.*; +import org.eclipse.osgi.util.NLS; import org.osgi.framework.BundleException; +import org.osgi.service.log.LogService; public class SharedConfigurationTest extends AbstractFwkAdminTest { public SharedConfigurationTest(String name) { super(name); } - + public void testDefaultConfiguration() throws IllegalStateException, FrameworkAdminRuntimeException, IOException, BundleException { startSimpleConfiguratorManipulator(); FrameworkAdmin fwkAdmin = getEquinoxFrameworkAdmin(); @@ -30,19 +39,19 @@ public class SharedConfigurationTest extends AbstractFwkAdminTest { File defaultConfigurationFolder = new File(installFolder, "configuration"); defaultConfigurationFolder.mkdirs(); copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/config.ini"), new File(defaultConfigurationFolder, "config.ini")); - + String launcherName = "foo"; LauncherData launcherData = manipulator.getLauncherData(); launcherData.setFwConfigLocation(defaultConfigurationFolder); launcherData.setLauncher(new File(installFolder, launcherName)); - + try { manipulator.load(); } catch (IllegalStateException e) { //TODO We ignore the framework JAR location not set exception } - + assertEquals("false", manipulator.getConfigData().getProperty("config.shared")); assertEquals("true", manipulator.getConfigData().getProperty("from.parent")); } @@ -59,23 +68,23 @@ public class SharedConfigurationTest extends AbstractFwkAdminTest { File userConfigurationFolder = new File(installFolder, "user/configuration"); userConfigurationFolder.mkdirs(); copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/user-config.ini"), new File(userConfigurationFolder, "config.ini")); - + String launcherName = "foo"; LauncherData launcherData = manipulator.getLauncherData(); launcherData.setFwConfigLocation(userConfigurationFolder); launcherData.setLauncher(new File(installFolder, launcherName)); - + try { manipulator.load(); } catch (IllegalStateException e) { //TODO We ignore the framework JAR location not set exception } - + assertEquals("true", manipulator.getConfigData().getProperty("config.shared")); assertEquals("true", manipulator.getConfigData().getProperty("from.parent")); } - + public void testNotSharedConfiguration() throws IllegalStateException, FrameworkAdminRuntimeException, IOException, BundleException { startSimpleConfiguratorManipulator(); FrameworkAdmin fwkAdmin = getEquinoxFrameworkAdmin(); @@ -88,20 +97,109 @@ public class SharedConfigurationTest extends AbstractFwkAdminTest { File userConfigurationFolder = new File(installFolder, "user/configuration"); userConfigurationFolder.mkdirs(); copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/user-noshare-config.ini"), new File(userConfigurationFolder, "config.ini")); - + String launcherName = "foo"; LauncherData launcherData = manipulator.getLauncherData(); launcherData.setFwConfigLocation(userConfigurationFolder); launcherData.setLauncher(new File(installFolder, launcherName)); - + try { manipulator.load(); } catch (IllegalStateException e) { //TODO We ignore the framework JAR location not set exception } - + assertEquals("false", manipulator.getConfigData().getProperty("config.shared")); assertEquals(null, manipulator.getConfigData().getProperty("from.parent")); } + + public void testConfigIniTimestamp() throws BundleException, FrameworkAdminRuntimeException, IOException, URISyntaxException { + startSimpleConfiguratorManipulator(); + FrameworkAdmin fwkAdmin = getEquinoxFrameworkAdmin(); + Manipulator manipulator = fwkAdmin.getManipulator(); + + File installFolder = Activator.getContext().getDataFile(SharedConfigurationTest.class.getName()); + File defaultConfigurationFolder = new File(installFolder, "configuration"); + defaultConfigurationFolder.mkdirs(); + copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/config.ini"), new File(defaultConfigurationFolder, "config.ini")); + File userConfigurationFolder = new File(installFolder, "user/configuration"); + userConfigurationFolder.mkdirs(); + copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/user-config.ini"), new File(userConfigurationFolder, "config.ini")); + + String launcherName = "foo"; + + LauncherData launcherData = manipulator.getLauncherData(); + launcherData.setFwConfigLocation(userConfigurationFolder); + launcherData.setLauncher(new File(installFolder, launcherName)); + + try { + manipulator.load(); + } catch (IllegalStateException e) { + //TODO We ignore the framework JAR location not set exception + } + + BundleInfo osgiBi = new BundleInfo("org.eclipse.osgi", "3.3.1", URIUtil.toURI(FileLocator.resolve(Activator.getContext().getBundle().getEntry("dataFile/org.eclipse.osgi.jar"))), 0, true); + BundleInfo configuratorBi = new BundleInfo("org.eclipse.equinox.simpleconfigurator", "1.0.0", URIUtil.toURI(FileLocator.resolve(Activator.getContext().getBundle().getEntry("dataFile/org.eclipse.equinox.simpleconfigurator.jar"))), 1, true); + + manipulator.getConfigData().addBundle(osgiBi); + manipulator.getConfigData().addBundle(configuratorBi); + + manipulator.save(false); + File baseTimestamp = new File(userConfigurationFolder, ".baseTimestamps"); + assertIsFile(baseTimestamp); + assertContent(baseTimestamp, Long.toString(new File(defaultConfigurationFolder, "config.ini").lastModified())); + } + + public void testConfigurationIgnoredWhenChanged() throws BundleException, FrameworkAdminRuntimeException, IOException { + startSimpleConfiguratorManipulator(); + FrameworkAdmin fwkAdmin = getEquinoxFrameworkAdmin(); + Manipulator manipulator = fwkAdmin.getManipulator(); + + //setup the files + File installFolder = Activator.getContext().getDataFile(SharedConfigurationTest.class.getName()); + File defaultConfigurationFolder = new File(installFolder, "configuration"); + defaultConfigurationFolder.mkdirs(); + copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/ignoreUserConfig/config.ini"), new File(defaultConfigurationFolder, "config.ini")); + File userConfigurationFolder = new File(installFolder, "user/configuration"); + userConfigurationFolder.mkdirs(); + copy("creating shared config.ini", getTestData("", "dataFile/sharedconfiguration/ignoreUserConfig/user-config.ini"), new File(userConfigurationFolder, "config.ini")); + + //setup the timestamp + Properties p = new Properties(); + p.setProperty("configIniTimestamp", Long.toString(new File(defaultConfigurationFolder, "config.ini").lastModified())); + saveProperties(new File(userConfigurationFolder, ".baseTimestamps"), p); + + String launcherName = "foo"; + + LauncherData launcherData = manipulator.getLauncherData(); + launcherData.setFwConfigLocation(userConfigurationFolder); + launcherData.setLauncher(new File(installFolder, launcherName)); + + try { + manipulator.load(); + } catch (IllegalStateException e) { + //TODO We ignore the framework JAR location not set exception + } + + assertNull(manipulator.getConfigData().getProperty("userKey")); + assertEquals("sharedValue", manipulator.getConfigData().getProperty("sharedKey")); + } + + 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(); + } + } + } } diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/SimpleProfileRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/SimpleProfileRegistry.java index 945ec048e..9e0265404 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/SimpleProfileRegistry.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/SimpleProfileRegistry.java @@ -1,12 +1,12 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 IBM Corporation and others. All rights reserved. This + * Copyright (c) 2007, 2013 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 - * Ericsson AB (Pascal Rapicault) - reading preferences from base in shared install + * Ericsson AB - ongoing development ******************************************************************************/ package org.eclipse.equinox.internal.p2.engine; @@ -38,6 +38,8 @@ import org.xml.sax.SAXException; public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { + private static final String SIMPLE_PROFILE_REGISTRY_INTERNAL = "_simpleProfileRegistry_internal_"; + private static final String ECLIPSE_IGNORE_USER_CONFIGURATION = "eclipse.ignoreUserConfiguration"; //$NON-NLS-1$ private static final String PROFILE_REGISTRY = "profile registry"; //$NON-NLS-1$ private static final String PROFILE_PROPERTIES_FILE = "state.properties"; //$NON-NLS-1$ @@ -168,7 +170,7 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { } public synchronized String toString() { - return "Profile registry for location: " + store.getAbsolutePath() + "\n" + getProfileMap().toString(); //$NON-NLS-1$ + return "Profile registry for location: " + store.getAbsolutePath() + "\n" + getProfileMap().toString(); //$NON-NLS-1$ //$NON-NLS-2$ } public synchronized IProfile getProfile(String id) { @@ -243,12 +245,57 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { if (SELF.equals(id)) id = self; Profile profile = getProfileMap().get(id); - if (profile == null && self != null && self.equals(id)) - profile = createSurrogateProfile(id); - + if (self != null && self.equals(id)) { + boolean resetProfile = false; + if (profile != null && ignoreExistingProfile(profile)) { + internalSetProfileStateProperty(profile, profile.getTimestamp(), IProfile.STATE_PROP_SHARED_INSTALL, IProfile.STATE_SHARED_INSTALL_VALUE_BEFOREFLUSH); + profile = null; + resetProfile = true; + } + if (profile == null) { + profile = createSurrogateProfile(id); + if (resetProfile) { + //Now that we created a new profile. Tag it, override the property and register the timestamp in the agent registry for pickup by other + internalSetProfileStateProperty(profile, profile.getTimestamp(), IProfile.STATE_PROP_SHARED_INSTALL, IProfile.STATE_SHARED_INSTALL_VALUE_NEW); + internalSetProfileStateProperty(profile, profile.getTimestamp(), SIMPLE_PROFILE_REGISTRY_INTERNAL + getBaseTimestamp(profile.getProfileId()), getBaseTimestamp(id)); + System.setProperty(ECLIPSE_IGNORE_USER_CONFIGURATION, "profileFlushed"); //$NON-NLS-1$ + agent.registerService(SERVICE_SHARED_INSTALL_NEW_TIMESTAMP, Long.toString(profile.getTimestamp())); + } else { + //This is the first time we create the shared profile. Tag it as such and also remember the timestamp of the base + internalSetProfileStateProperty(profile, profile.getTimestamp(), IProfile.STATE_PROP_SHARED_INSTALL, IProfile.STATE_SHARED_INSTALL_VALUE_INITIAL); + internalSetProfileStateProperty(profile, profile.getTimestamp(), SIMPLE_PROFILE_REGISTRY_INTERNAL + getBaseTimestamp(profile.getProfileId()), getBaseTimestamp(id)); + } + } + } return profile; } + private boolean ignoreExistingProfile(IProfile profile) { + if (!Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(ECLIPSE_IGNORE_USER_CONFIGURATION))) + return false; + + String baseTimestamp = getBaseTimestamp(profile.getProfileId()); + if (baseTimestamp == null) + return false; + + if (internalGetProfileStateProperties(profile, SIMPLE_PROFILE_REGISTRY_INTERNAL + baseTimestamp, false).size() != 0) + return false; + + return true; + } + + private String getBaseTimestamp(String id) { + IProvisioningAgent installer = (IProvisioningAgent) agent.getService(IProvisioningAgent.SHARED_BASE_AGENT); + if (installer == null) + return null; + IProfileRegistry registry = (IProfileRegistry) installer.getService(IProfileRegistry.SERVICE_NAME); + long[] revisions = registry.listProfileTimestamps(id); + if (revisions.length >= 1) { + return Long.toString(revisions[revisions.length - 1]); + } + return null; + } + private Profile createSurrogateProfile(String id) { if (surrogateProfileHandler == null) return null; @@ -294,7 +341,7 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { public synchronized void updateProfile(Profile profile) { String id = profile.getProfileId(); - Profile current = internalGetProfile(id); + Profile current = getProfileMap().get(id); if (current == null) throw new IllegalArgumentException(NLS.bind(Messages.profile_does_not_exist, id)); @@ -730,7 +777,7 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { } public synchronized boolean isCurrent(IProfile profile) { - Profile internalProfile = internalGetProfile(profile.getProfileId()); + Profile internalProfile = getProfileMap().get(profile.getProfileId()); if (internalProfile == null) throw new IllegalArgumentException(NLS.bind(Messages.profile_not_registered, profile.getProfileId())); @@ -833,10 +880,9 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { } public synchronized void unlockProfile(IProfile profile) { - Profile internalProfile = internalGetProfile(profile.getProfileId()); - if (internalProfile == null) + if (profile == null) throw new IllegalArgumentException(NLS.bind(Messages.profile_not_registered, profile.getProfileId())); - internalUnlockProfile(internalProfile); + internalUnlockProfile(profile); } private void internalUnlockProfile(IProfile profile) { @@ -897,6 +943,8 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { // return true if the cached timestamp is the same as the one on disk boolean isCurrent() { + if (!file.exists()) + return true; return file.lastModified() == timestamp; } @@ -936,8 +984,10 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { File file = new File(profileDirectory, PROFILE_PROPERTIES_FILE); Properties properties = new Properties(); - if (!file.exists()) + if (!file.exists()) { + lastAccessedProperties = new ProfileStateProperties(id, file, properties); return properties; + } InputStream input = null; try { input = new BufferedInputStream(new FileInputStream(file)); @@ -1032,22 +1082,30 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { public IStatus setProfileStateProperties(String id, long timestamp, Map<String, String> propertiesToAdd) { if (id == null || propertiesToAdd == null) throw new NullPointerException(); - IStatus result = validateState(id, timestamp); + + Profile internalProfile = internalGetProfile(id); + return internalSetProfileStateProperties(internalProfile, timestamp, propertiesToAdd); + } + + private IStatus internalSetProfileStateProperties(IProfile profile, long timestamp, Map<String, String> propertiesToAdd) { + IStatus result = validateState(profile.getProfileId(), timestamp); if (!result.isOK()) return result; - Profile internalProfile = internalGetProfile(id); - lockProfile(internalProfile); + + if (!internalLockProfile(profile)) + throw new IllegalStateException(Messages.SimpleProfileRegistry_Profile_in_use); + try { - Properties properties = readStateProperties(id); + Properties properties = readStateProperties(profile.getProfileId()); for (Map.Entry<String, String> entry : propertiesToAdd.entrySet()) { // property key format is timestamp.key properties.put(timestamp + "." + entry.getKey(), entry.getValue()); //$NON-NLS-1$ } - writeStateProperties(id, properties); + writeStateProperties(profile.getProfileId(), properties); } catch (ProvisionException e) { return e.getStatus(); } finally { - unlockProfile(internalProfile); + internalUnlockProfile(profile); } return Status.OK_STATUS; } @@ -1056,27 +1114,36 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { * @see org.eclipse.equinox.p2.engine.IProfileRegistry#setProfileStateProperty(java.lang.String, long, java.lang.String, java.lang.String) */ public IStatus setProfileStateProperty(String id, long timestamp, String key, String value) { - if (id == null || key == null || value == null) + if (id == null) + throw new NullPointerException(); + return internalSetProfileStateProperty(internalGetProfile(id), timestamp, key, value); + } + + private IStatus internalSetProfileStateProperty(IProfile profile, long timestamp, String key, String value) { + if (key == null || value == null) throw new NullPointerException(); Map<String, String> properties = new HashMap<String, String>(); properties.put(key, value); - return setProfileStateProperties(id, timestamp, properties); + + return internalSetProfileStateProperties(profile, timestamp, properties); } - /* (non-Javadoc) - * @see org.eclipse.equinox.p2.engine.IProfileRegistry#getProfileStateProperties(java.lang.String, long) - */ public Map<String, String> getProfileStateProperties(String id, long timestamp) { if (id == null) throw new NullPointerException(); + return internalGetProfileStateProperties(internalGetProfile(id), timestamp, true); + } + private Map<String, String> internalGetProfileStateProperties(IProfile profile, long timestamp, boolean lock) { Map<String, String> result = new HashMap<String, String>(); String timestampString = String.valueOf(timestamp); int keyOffset = timestampString.length() + 1; - Profile internalProfile = internalGetProfile(id); - lockProfile(internalProfile); + lock = lock || lastAccessedProperties == null; + if (lock) + if (!internalLockProfile(profile)) + throw new IllegalStateException(Messages.SimpleProfileRegistry_Profile_in_use); try { - Properties properties = readStateProperties(id); + Properties properties = readStateProperties(profile.getProfileId()); Iterator<Object> keys = properties.keySet().iterator(); while (keys.hasNext()) { String key = (String) keys.next(); @@ -1086,7 +1153,8 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { } catch (ProvisionException e) { LogHelper.log(e); } finally { - unlockProfile(internalProfile); + if (lock) + internalUnlockProfile(profile); } return result; } @@ -1098,11 +1166,18 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { if (id == null || userKey == null) throw new NullPointerException(); - Map<String, String> result = new HashMap<String, String>(); Profile internalProfile = internalGetProfile(id); - lockProfile(internalProfile); + return internalGetProfileStateProperties(internalProfile, userKey, true); + } + + private Map<String, String> internalGetProfileStateProperties(IProfile profile, String userKey, boolean lock) { + Map<String, String> result = new HashMap<String, String>(); + lock = lock || lastAccessedProperties == null; + if (lock) + if (!internalLockProfile(profile)) + throw new IllegalStateException(Messages.SimpleProfileRegistry_Profile_in_use); try { - Properties properties = readStateProperties(id); + Properties properties = readStateProperties(profile.getProfileId()); Iterator<Object> keys = properties.keySet().iterator(); while (keys.hasNext()) { // property key format is timestamp.key @@ -1115,7 +1190,8 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { } catch (ProvisionException e) { LogHelper.log(e); } finally { - unlockProfile(internalProfile); + if (lock) + internalUnlockProfile(profile); } return result; } @@ -1131,7 +1207,9 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { return Status.OK_STATUS; Profile internalProfile = internalGetProfile(id); - lockProfile(internalProfile); + if (!internalLockProfile(internalProfile)) + throw new IllegalStateException(Messages.SimpleProfileRegistry_Profile_in_use); + try { Properties properties = readStateProperties(id); String timestampString = String.valueOf(timestamp); @@ -1154,7 +1232,7 @@ public class SimpleProfileRegistry implements IProfileRegistry, IAgentService { } catch (ProvisionException e) { return e.getStatus(); } finally { - unlockProfile(internalProfile); + internalUnlockProfile(internalProfile); } return Status.OK_STATUS; } diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfile.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfile.java index 1f8de7645..7bd0060c2 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfile.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfile.java @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ericsson AB - ongoing development *******************************************************************************/ package org.eclipse.equinox.p2.engine; @@ -146,6 +147,29 @@ public interface IProfile extends IQueryable<IInstallableUnit> { public static final String STATE_PROP_TAG = "org.eclipse.equinox.p2.state.tag"; //$NON-NLS-1$ /** + * Profile state metadata property key used to represent the state of the user profile when running in shared install. + * The value for this property could be: {@value #STATE_PROP_INITIAL}, {@value #STATE_SHARED_INSTALL_VALUE_BEFOREFLUSH}, or {@value #STATE_SHARED_INSTALL_VALUE_NEW} + * @since 2.3 + */ + public static final String STATE_PROP_SHARED_INSTALL = "org.eclipse.equinox.p2.state.shared"; //$NON-NLS-1$ + + /** + * Value to represent a user profile the first time it is created. + * @since 2.3 + */ + public static final String STATE_SHARED_INSTALL_VALUE_INITIAL = "initial"; //$NON-NLS-1$ + /** + * Value to represent a user profile before it is being flushed because the base had changed. + * @since 2.3 + */ + public static final String STATE_SHARED_INSTALL_VALUE_BEFOREFLUSH = "beforeFlush"; //$NON-NLS-1$ + /** + * Value to represent the new user profile created once the base profile has been flushed. + * @since 2.3 + */ + public static final String STATE_SHARED_INSTALL_VALUE_NEW = "new"; //$NON-NLS-1$ + + /** * Profile property constant for additional parameters of the downloading stats(e.g., package=jee&os=linux). * @since 2.2 */ diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfileRegistry.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfileRegistry.java index 381a61e20..b2490c894 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfileRegistry.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine/IProfileRegistry.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ericsson AB - ongoing development *******************************************************************************/ package org.eclipse.equinox.p2.engine; @@ -37,6 +38,8 @@ public interface IProfileRegistry { */ public static final String SERVICE_NAME = IProfileRegistry.class.getName(); + public static final String SERVICE_SHARED_INSTALL_NEW_TIMESTAMP = IProfileRegistry.class.getName() + '_' + "NEW_SELF_TIMESTAMP"; //$NON-NLS-1$ + /** * Return the profile in the registry that has the given id. If it does not exist, * then return <code>null</code>. diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Activator.java b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Activator.java index d8da4f56a..800871953 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Activator.java +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Activator.java @@ -227,6 +227,13 @@ public class Activator implements BundleActivator { trace("Performing reconciliation."); //$NON-NLS-1$ return false; } + // master configuration changed. Reconcile. + if (Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(ProfileSynchronizer.PROP_IGNORE_USER_CONFIGURATION))) { + Activator.trace("Master profile changed."); //$NON-NLS-1$ + Activator.trace("Performing reconciliation."); //$NON-NLS-1$ + return false; + } + // read timestamps Properties timestamps = readTimestamps(); if (timestamps.isEmpty()) { diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java index 337d41a86..cf3feb315 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 IBM Corporation and others. All rights reserved. + * Copyright (c) 2007, 2013 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 @@ -7,11 +7,10 @@ * Contributors: * IBM Corporation - initial implementation and ideas * Sonatype, Inc. - ongoing development + * RedHat, Inc. - Bug 397216 ******************************************************************************/ package org.eclipse.equinox.internal.p2.reconciler.dropins; -import org.eclipse.equinox.p2.metadata.IRequirement; - import java.io.*; import java.net.*; import java.util.*; @@ -56,6 +55,9 @@ public class ProfileSynchronizer { private static final String CACHE_EXTENSIONS = "org.eclipse.equinox.p2.cache.extensions"; //$NON-NLS-1$ private static final String PIPE = "|"; //$NON-NLS-1$ private static final String EXPLANATION = "org.eclipse.equinox.p2.director.explain"; //$NON-NLS-1$ + + static final String PROP_IGNORE_USER_CONFIGURATION = "eclipse.ignoreUserConfiguration"; //$NON-NLS-1$ + final IProfile profile; final Map<String, IMetadataRepository> repositoryMap; @@ -396,6 +398,12 @@ public class ProfileSynchronizer { * Read the values of the stored timestamps that we use for caching. */ private void readTimestamps() { + if (Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(PROP_IGNORE_USER_CONFIGURATION))) { + timestamps = new HashMap<String, String>(); + Activator.trace("Master profile changed."); //$NON-NLS-1$ + Activator.trace("Performing reconciliation."); //$NON-NLS-1$ + return; + } File file = Activator.getContext().getDataFile(TIMESTAMPS_FILE_PREFIX + profile.getProfileId().hashCode()); try { InputStream is = new BufferedInputStream(new FileInputStream(file)); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java new file mode 100644 index 000000000..0d964ac49 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTest.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2012 Red Hat, Inc. 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: Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package org.eclipse.equinox.p2.tests.simpleconfigurator; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Properties; +import org.eclipse.equinox.internal.simpleconfigurator.SimpleConfiguratorImpl; +import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; + +public class SimpleConfiguratorTest extends AbstractProvisioningTest { + + private URL relativeURL; + private File userConfiguration; + private File masterConfguration; + private URL[] sharedConfiguration = new URL[2]; + private URL[] localConfiguration = new URL[1]; + private SimpleConfiguratorImpl configurator; + + public void setUp() throws Exception { + relativeURL = new URL("file://bundles.info"); + userConfiguration = getTestData("userConfiguration", "testData/simpleconfigurator/user"); + sharedConfiguration[0] = userConfiguration.toURL(); + masterConfguration = getTestData("userConfiguration", "testData/simpleconfigurator/master"); + sharedConfiguration[1] = masterConfguration.toURL(); + localConfiguration[0] = sharedConfiguration[1]; + configurator = getSimpleConfigurator(); + } + + private SimpleConfiguratorImpl getSimpleConfigurator() { + return new SimpleConfiguratorImpl(null, null); + } + + private void storeTimestamp(long timestamp) throws IOException { + File f = new File(userConfiguration.getParent(), SimpleConfiguratorImpl.BASE_TIMESTAMP_FILE_BUNDLESINFO); + Properties p = new Properties(); + p.put(SimpleConfiguratorImpl.KEY_BUNDLESINFO_TIMESTAMP, "" + timestamp); + p.store(new FileWriter(f), ""); + } + + @Override + protected void tearDown() throws Exception { + System.setProperty(SimpleConfiguratorImpl.PROP_IGNORE_USER_CONFIGURATION, "false"); + File f = new File(userConfiguration.getParent(), SimpleConfiguratorImpl.BASE_TIMESTAMP_FILE_BUNDLESINFO); + if (f.exists()) { + f.delete(); + } + super.tearDown(); + } + + private void assertIsPropertySet(boolean set) { + assertEquals(set, Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(SimpleConfiguratorImpl.PROP_IGNORE_USER_CONFIGURATION))); + } + + public void testSimpleConfiguration() throws MalformedURLException { + assertEquals(localConfiguration[0], configurator.chooseConfigurationURL(relativeURL, localConfiguration)); + assertIsPropertySet(false); + } + + public void testNotExistingConfigiration() throws MalformedURLException { + assertNull(configurator.chooseConfigurationURL(relativeURL, new URL[] {new File(".", "notexisting").toURL()})); + assertIsPropertySet(false); + } + + public void testSharedConfigurationUserNotExisting() throws MalformedURLException { + sharedConfiguration[0] = new File(".", "notexisting").toURL(); + assertEquals(sharedConfiguration[1], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration)); + assertIsPropertySet(false); + } + + // no timestamp -> pick user + public void testSharedConfigurationNoTimestamp() throws MalformedURLException { + assertEquals(sharedConfiguration[0], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration)); + assertIsPropertySet(false); + } + + //master modified -> pick master + public void testSharedConfigurationMasterModified() throws IOException { + storeTimestamp(1000); + assertEquals(sharedConfiguration[1], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration)); + assertIsPropertySet(true); + } + + //master not modified -> pick user + public void testSharedConfigurationMasterUnmodified() throws IOException { + storeTimestamp(new File(masterConfguration, relativeURL.getFile()).lastModified()); + assertEquals(sharedConfiguration[0], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration)); + assertIsPropertySet(false); + } + + //master not modified, but property present -> pick master + public void testSharedConfigurationMasterUnmodifiedPropertySet() throws IOException { + System.setProperty(SimpleConfiguratorImpl.PROP_IGNORE_USER_CONFIGURATION, "true"); + storeTimestamp(new File(masterConfguration, relativeURL.getFile()).lastModified()); + assertEquals(sharedConfiguration[1], configurator.chooseConfigurationURL(relativeURL, sharedConfiguration)); + assertIsPropertySet(true); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTests.java index a3b448fa5..454f30d8c 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTests.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/simpleconfigurator/SimpleConfiguratorTests.java @@ -16,6 +16,7 @@ public class SimpleConfiguratorTests { public static Test suite() { TestSuite suite = new TestSuite("Tests for org.eclipse.equinox.simpleconfigurator"); //$JUnit-BEGIN$ + suite.addTestSuite(SimpleConfiguratorTest.class); suite.addTestSuite(SimpleConfiguratorUtilsTest.class); suite.addTestSuite(BundlesTxtTest.class); suite.addTestSuite(NonExclusiveMode.class); diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/simpleconfigurator/master/bundles.info b/bundles/org.eclipse.equinox.p2.tests/testData/simpleconfigurator/master/bundles.info new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/simpleconfigurator/master/bundles.info diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/simpleconfigurator/user/bundles.info b/bundles/org.eclipse.equinox.p2.tests/testData/simpleconfigurator/user/bundles.info new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/simpleconfigurator/user/bundles.info diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java index 785a01845..06186bb8b 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2011 IBM Corporation and others. + * Copyright (c) 2008, 2013 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ericsson AB - ongoing development *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.sdk.scheduler; @@ -22,8 +23,11 @@ import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.ui.IStartup; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.*; import org.eclipse.ui.statushandlers.StatusManager; /** @@ -78,10 +82,46 @@ public class AutomaticUpdateScheduler implements IStartup { } public void earlyStartup() { + if (baseChanged()) + return; garbageCollect(); scheduleUpdate(); } + Shell getWorkbenchWindowShell() { + IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + return activeWindow != null ? activeWindow.getShell() : null; + + } + + private boolean baseChanged() { + IProvisioningAgent agent = (IProvisioningAgent) ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.SERVICE_NAME); + IProfileRegistry registry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME); + //Access the running profile to force its reinitialization if it has not been done. + registry.getProfile(IProfileRegistry.SELF); + String resetState = (String) agent.getService(IProfileRegistry.SERVICE_SHARED_INSTALL_NEW_TIMESTAMP); + if (resetState == null) + return false; + + final String PREF_MIGRATION_DIALOG_SHOWN = "migrationDialogShown"; //$NON-NLS-1$ + + //Have we already shown the migration dialog + if (AutomaticUpdatePlugin.getDefault().getPreferenceStore().getString(PREF_MIGRATION_DIALOG_SHOWN) == resetState) + return false; + + //Remember that we are showing the migration dialog + AutomaticUpdatePlugin.getDefault().getPreferenceStore().setValue(PREF_MIGRATION_DIALOG_SHOWN, resetState); + AutomaticUpdatePlugin.getDefault().savePreferences(); + + Display d = Display.getDefault(); + d.asyncExec(new Runnable() { + public void run() { + MessageDialog.openWarning(getWorkbenchWindowShell(), "Installation modified", "An upgrade of the eclipse installation you are using has been performed. The plugins you had installed have been uninstalled."); //$NON-NLS-1$ //$NON-NLS-2$ + } + }); + return true; + } + /** * Invokes the garbage collector to discard unused plugins, if specified by a * corresponding preference. diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/RepositoryManipulationPage.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/RepositoryManipulationPage.java index 73b63c7a8..914f56c2d 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/RepositoryManipulationPage.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/RepositoryManipulationPage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ericsson AB (Pascal Rapicault) - bug 398539 *******************************************************************************/ package org.eclipse.equinox.p2.ui; @@ -774,7 +775,7 @@ public class RepositoryManipulationPage extends PreferencePage implements IWorkb }; int retCode = dialog.open(); - if (retCode == Window.OK) { + if (retCode == Window.OK && dialog.getLocation() != null) { selected[0].setNickname(dialog.getName()); selected[0].setLocation(dialog.getLocation()); if (dialog.getLocation().equals(existingLocation)) { diff --git a/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/src/org/eclipse/equinox/internal/simpleconfigurator/manipulator/SimpleConfiguratorManipulatorImpl.java b/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/src/org/eclipse/equinox/internal/simpleconfigurator/manipulator/SimpleConfiguratorManipulatorImpl.java index c6748e918..55e1743dd 100644 --- a/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/src/org/eclipse/equinox/internal/simpleconfigurator/manipulator/SimpleConfiguratorManipulatorImpl.java +++ b/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/src/org/eclipse/equinox/internal/simpleconfigurator/manipulator/SimpleConfiguratorManipulatorImpl.java @@ -1,10 +1,13 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. All rights reserved. + * Copyright (c) 2007, 2013 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 + * + * Ericsson AB (Pascal Rapicault) - Bug 397216 -[Shared] Better shared + * configuration change discovery *******************************************************************************/ package org.eclipse.equinox.internal.simpleconfigurator.manipulator; @@ -42,8 +45,10 @@ public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorMani 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 CONFIG_FOLDER = "configuration"; //$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$ + public static final String SHARED_BUNDLES_INFO = CONFIG_FOLDER + File.separatorChar + CONFIGURATOR_FOLDER + File.separatorChar + CONFIG_LIST; private Set manipulators = new HashSet(); @@ -63,7 +68,7 @@ public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorMani if (manipulator.getLauncherData().getLauncher() != null) { baseDir = manipulator.getLauncherData().getLauncher().getParentFile(); } else { - throw new IllegalStateException("All of fwConfigFile, home, launcher are not set."); + throw new IllegalStateException("All of fwConfigFile, home, launcher are not set."); //$NON-NLS-1$ } } } else { @@ -393,6 +398,33 @@ public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorMani return; } SimpleConfiguratorManipulatorUtils.writeConfiguration(simpleInfos, outputFile); + if (CONFIG_LIST.equals(outputFile.getName()) && isSharedInstallSetup(URIUtil.toFile(installArea), outputFile)) + rememberSharedBundlesInfoTimestamp(installArea, outputFile.getParentFile()); + } + + private void rememberSharedBundlesInfoTimestamp(URI installArea, File outputFolder) { + if (installArea == null) + return; + + File sharedBundlesInfo = new File(URIUtil.append(installArea, SHARED_BUNDLES_INFO)); + if (!sharedBundlesInfo.exists()) + return; + + Properties timestampToPersist = new Properties(); + timestampToPersist.put(SimpleConfiguratorImpl.KEY_BUNDLESINFO_TIMESTAMP, Long.toString(sharedBundlesInfo.lastModified())); + OutputStream os = null; + try { + try { + File outputFile = new File(outputFolder, SimpleConfiguratorImpl.BASE_TIMESTAMP_FILE_BUNDLESINFO); + os = new BufferedOutputStream(new FileOutputStream(outputFile)); + timestampToPersist.store(os, "Written by " + this.getClass()); //$NON-NLS-1$ + } finally { + if (os != null) + os.close(); + } + } catch (IOException e) { + return; + } } private org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo[] convertBundleInfos(BundleInfo[] configuration, URI installArea) { @@ -402,7 +434,7 @@ public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorMani BundleInfo bundleInfo = configuration[i]; URI location = bundleInfo.getLocation(); if (bundleInfo.getSymbolicName() == null || bundleInfo.getVersion() == null || location == null) - throw new IllegalArgumentException("Cannot persist bundleinfo: " + bundleInfo.toString()); + throw new IllegalArgumentException("Cannot persist bundleinfo: " + bundleInfo.toString()); //$NON-NLS-1$ //only need to make a new BundleInfo if we are changing it. if (installArea != null) location = URIUtil.makeRelative(location, installArea); @@ -492,13 +524,17 @@ public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorMani File configFile = getConfigFile(manipulator); File installArea = ParserUtils.getOSGiInstallArea(Arrays.asList(manipulator.getLauncherData().getProgramArgs()), manipulator.getConfigData().getProperties(), manipulator.getLauncherData()); - BundleInfo[] toInstall = null; - try { - //input stream will be closed for us - toInstall = loadConfiguration(new FileInputStream(configFile), installArea.toURI()); - } catch (FileNotFoundException e) { - //no file, just return an empty list - toInstall = new BundleInfo[0]; + BundleInfo[] toInstall = new BundleInfo[0]; + + boolean isShared = isSharedInstallSetup(installArea, configFile); + if (!isShared || (isShared && !hasBaseChanged(installArea.toURI(), configFile.getParentFile()))) { + try { + //input stream will be closed for us + toInstall = loadConfiguration(new FileInputStream(configFile), installArea.toURI()); + } catch (FileNotFoundException e) { + //no file, just return an empty list + toInstall = new BundleInfo[0]; + } } List toUninstall = new LinkedList(); @@ -538,4 +574,43 @@ public class SimpleConfiguratorManipulatorImpl implements SimpleConfiguratorMani if (outputFile.getParentFile().isDirectory()) outputFile.getParentFile().delete(); } + + private boolean hasBaseChanged(URI installArea, File outputFolder) { + String rememberedTimestamp; + try { + rememberedTimestamp = (String) loadProperties(new File(outputFolder, SimpleConfiguratorImpl.BASE_TIMESTAMP_FILE_BUNDLESINFO)).get(SimpleConfiguratorImpl.KEY_BUNDLESINFO_TIMESTAMP); + } catch (IOException e) { + return false; + } + if (rememberedTimestamp == null) + return false; + + File sharedBundlesInfo = new File(URIUtil.append(installArea, SHARED_BUNDLES_INFO)); + if (!sharedBundlesInfo.exists()) + return true; + return !String.valueOf(sharedBundlesInfo.lastModified()).equals(rememberedTimestamp); + } + + private boolean isSharedInstallSetup(File installArea, File outputFile) { + //An instance is treated as shared if the bundles.info file is not located in the install area. + return !new File(installArea, SHARED_BUNDLES_INFO).equals(outputFile); + } + + private 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) { + //Do nothing + } + is = null; + } + return props; + } } diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java b/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java index dbd2aec45..70d3b4c93 100644 --- a/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java +++ b/bundles/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/SimpleConfiguratorImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,13 +7,14 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Ericsson AB (Pascal Rapicault) - Bug 397216 -[Shared] Better shared configuration change discovery *******************************************************************************/ package org.eclipse.equinox.internal.simpleconfigurator; -import java.io.File; -import java.io.IOException; -import java.net.*; -import java.util.List; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Properties; import org.eclipse.equinox.internal.provisional.configurator.Configurator; import org.eclipse.equinox.internal.simpleconfigurator.utils.*; import org.osgi.framework.Bundle; @@ -42,6 +43,12 @@ public class SimpleConfiguratorImpl implements Configurator { private ConfigApplier configApplier; private Bundle bundle; + //for change detection in the base when running in shared install mode + private static final long NO_TIMESTAMP = -1; + public static final String BASE_TIMESTAMP_FILE_BUNDLESINFO = ".baseBundlesInfoTimestamp"; //$NON-NLS-1$ + public static final String KEY_BUNDLESINFO_TIMESTAMP = "bundlesInfoTimestamp"; + public static final String PROP_IGNORE_USER_CONFIGURATION = "eclipse.ignoreUserConfiguration"; //$NON-NLS-1$ + public SimpleConfiguratorImpl(BundleContext context, Bundle bundle) { this.context = context; this.bundle = bundle; @@ -80,27 +87,10 @@ public class SimpleConfiguratorImpl implements Configurator { //if it is an relative file URL, then resolve it against the configuration area // TODO Support relative file URLs when not on Equinox URL[] configURL = EquinoxUtils.getConfigAreaURL(context); - if (configURL != null) { - File userConfig = new File(configURL[0].getFile(), url.getFile()); - if (configURL.length == 1) - return userConfig.exists() ? userConfig.toURL() : null; - - File sharedConfig = new File(configURL[1].getFile(), url.getFile()); - if (!userConfig.exists()) - return sharedConfig.exists() ? sharedConfig.toURL() : null; - - if (!sharedConfig.exists()) - return userConfig.toURL(); - - URI base = EquinoxUtils.getInstallLocationURI(context); - - URL sharedConfigURL = sharedConfig.toURL(); - List sharedBundles = SimpleConfiguratorUtils.readConfiguration(sharedConfigURL, base); - URL userConfigURL = userConfig.toURL(); - List userBundles = SimpleConfiguratorUtils.readConfiguration(userConfigURL, base); - - return (userBundles.containsAll(sharedBundles)) ? userConfigURL : sharedConfigURL; + URL result = chooseConfigurationURL(url, configURL); + if (result != null) { + return result; } } catch (MalformedURLException e) { return null; @@ -116,6 +106,69 @@ public class SimpleConfiguratorImpl implements Configurator { return null; } + /** + * This method is public for testing purposes only. + * @param relativeURL - a relative URL of the configuration + * @param configURL - an array of parent config URLs to which relativeURL can be appended. + */ + public URL chooseConfigurationURL(URL relativeURL, URL[] configURL) throws MalformedURLException { + if (configURL != null) { + File userConfig = new File(configURL[0].getFile(), relativeURL.getFile()); + if (configURL.length == 1) + return userConfig.exists() ? userConfig.toURL() : null; + + File sharedConfig = new File(configURL[1].getFile(), relativeURL.getFile()); + if (!userConfig.exists()) + return sharedConfig.exists() ? sharedConfig.toURL() : null; + + if (!sharedConfig.exists()) + return userConfig.toURL(); + + if (Boolean.TRUE.toString().equals(System.getProperty(PROP_IGNORE_USER_CONFIGURATION))) + return sharedConfig.toURL(); + + long sharedBundlesInfoTimestamp = getCurrentBundlesInfoBaseTimestamp(sharedConfig); + long lastKnownBaseTimestamp = getLastKnownBundlesInfoBaseTimestamp(userConfig.getParentFile()); + + if (lastKnownBaseTimestamp == sharedBundlesInfoTimestamp || lastKnownBaseTimestamp == NO_TIMESTAMP) { + return userConfig.toURL(); + } else { + System.setProperty(PROP_IGNORE_USER_CONFIGURATION, Boolean.TRUE.toString()); + return sharedConfig.toURL(); + } + } + return null; + } + + private long getLastKnownBundlesInfoBaseTimestamp(File configFolder) { + File storedSharedTimestamp = new File(configFolder, BASE_TIMESTAMP_FILE_BUNDLESINFO); + if (!storedSharedTimestamp.exists()) + return NO_TIMESTAMP; + + Properties p = new Properties(); + InputStream is = null; + try { + try { + is = new BufferedInputStream(new FileInputStream(storedSharedTimestamp)); + p.load(is); + if (p.get(KEY_BUNDLESINFO_TIMESTAMP) != null) { + return Long.valueOf((String) p.get(KEY_BUNDLESINFO_TIMESTAMP)).longValue(); + } + } finally { + is.close(); + } + } catch (IOException e) { + return NO_TIMESTAMP; + } + return NO_TIMESTAMP; + } + + private long getCurrentBundlesInfoBaseTimestamp(File sharedBundlesInfo) { + if (!sharedBundlesInfo.exists()) + return NO_TIMESTAMP; + return sharedBundlesInfo.lastModified(); + } + public void applyConfiguration(URL url) throws IOException { synchronized (configurationLock) { if (Activator.DEBUG) |