@@ -83,7 +83,7 @@ public abstract class AbstractJavaProjectConfigurator extends AbstractProjectCon
8383 /**
8484 *
8585 */
86- private static final String MULTI_RELEASE_OUTPUT = "multiReleaseOutput" ;
86+ private static final String MULTI_RELEASE_OUTPUT = "multiReleaseOutput" ;
8787
8888 private static final IPath [] DEFAULT_INCLUSIONS = new IPath [0 ];
8989
@@ -1005,13 +1005,75 @@ protected IContainer getFolder(IProject project, String path) throws CoreExcepti
10051005 && !ResourcesPlugin .getWorkspace ().getRoot ().getLocation ().toPath ().equals (folderPath )) {
10061006 String linkName = projectLocation .relativize (folderPath ).toString ().replace ("/" , "_" );
10071007 IFolder folder = project .getFolder (linkName );
1008- folder . createLink ( folderPath .toUri (), IResource . REPLACE , null );
1008+ createLinkWithRetry ( folder , folderPath .toUri ());
10091009 folder .setPersistentProperty (LINKED_MAVEN_RESOURCE , "true" );
10101010 return folder ;
10111011 }
10121012 return project .getFolder (relativePath );
10131013 }
10141014
1015+ /**
1016+ * Creates a linked resource with retry logic to handle intermittent failures on Windows. The parent resource may not
1017+ * be accessible immediately after project creation.
1018+ *
1019+ * @param folder the folder to create the link for
1020+ * @param target the target URI for the link
1021+ * @throws CoreException if all retry attempts fail
1022+ */
1023+ private void createLinkWithRetry (IFolder folder , java .net .URI target ) throws CoreException {
1024+ int maxAttempts = 10 ;
1025+ long initialDelay = 100 ; // milliseconds
1026+
1027+ for (int attempt = 1 ; attempt <= maxAttempts ; attempt ++ ) {
1028+ try {
1029+ // Ensure the parent project is accessible before attempting to create the link
1030+ IProject project = folder .getProject ();
1031+ if (project != null && !project .isAccessible ()) {
1032+ if (attempt < maxAttempts ) {
1033+ log .debug ("Project {} is not accessible, waiting before retry..." , project .getName ());
1034+ sleepWithExponentialBackoff (initialDelay , attempt , folder .getFullPath ().toString ());
1035+ continue ;
1036+ }
1037+ }
1038+
1039+ folder .createLink (target , IResource .REPLACE , null );
1040+ if (attempt > 1 ) {
1041+ log .info ("Successfully created linked resource for {} after {} attempts" , folder .getFullPath (), attempt );
1042+ }
1043+ return ; // Success
1044+ } catch (CoreException e ) {
1045+ // Check if this is the specific error we want to retry
1046+ if (attempt < maxAttempts ) {
1047+ log .warn ("Failed to create linked resource for {} (attempt {}/{}): {}. Retrying..." , folder .getFullPath (),
1048+ attempt , maxAttempts , e .getMessage ());
1049+ sleepWithExponentialBackoff (initialDelay , attempt , folder .getFullPath ().toString ());
1050+ } else {
1051+ throw e ;
1052+ }
1053+ }
1054+ }
1055+ }
1056+
1057+ /**
1058+ * Sleeps for a duration calculated using exponential backoff.
1059+ *
1060+ * @param initialDelay the initial delay in milliseconds
1061+ * @param attempt the current attempt number (1-based)
1062+ * @param context context information for error messages
1063+ * @throws CoreException if interrupted during sleep
1064+ */
1065+ private void sleepWithExponentialBackoff (long initialDelay , int attempt , String context ) throws CoreException {
1066+ long delay = initialDelay * (1L << (attempt - 1 )); // exponential backoff: initialDelay * 2^(attempt-1)
1067+ try {
1068+ Thread .sleep (delay );
1069+ } catch (InterruptedException ie ) {
1070+ Thread .currentThread ().interrupt ();
1071+ throw new CoreException (org .eclipse .core .runtime .Status .error (
1072+ "Interrupted while waiting for project resource to become accessible during linked resource creation for "
1073+ + context ));
1074+ }
1075+ }
1076+
10151077 private static IPath getProjectRelativePath (IProject project , Path absolutePath ) {
10161078 Path basedir = project .getLocation ().toPath ().toAbsolutePath ();
10171079 if (absolutePath .equals (basedir )) {
0 commit comments