22
22
import java .nio .charset .StandardCharsets ;
23
23
import java .text .MessageFormat ;
24
24
import java .util .ArrayList ;
25
+ import java .util .Comparator ;
25
26
import java .util .HashMap ;
27
+ import java .util .LinkedHashMap ;
26
28
import java .util .List ;
27
29
import java .util .Map ;
30
+ import java .util .Optional ;
28
31
29
32
import javax .xml .parsers .DocumentBuilder ;
30
33
import javax .xml .parsers .FactoryConfigurationError ;
@@ -170,6 +173,14 @@ public class DebugPlugin extends Plugin {
170
173
*/
171
174
public static final String EXTENSION_POINT_PROCESS_FACTORIES = "processFactories" ; //$NON-NLS-1$
172
175
176
+ /**
177
+ * Simple identifier constant (value <code>"execFactories"</code>) for the
178
+ * exec factories extension point.
179
+ *
180
+ * @since 3.23
181
+ */
182
+ public static final String EXTENSION_POINT_EXEC_FACTORIES = "execFactories" ; //$NON-NLS-1$
183
+
173
184
/**
174
185
* Simple identifier constant (value <code>"logicalStructureTypes"</code>) for the
175
186
* logical structure types extension point.
@@ -438,6 +449,11 @@ public class DebugPlugin extends Plugin {
438
449
*/
439
450
private HashMap <String , IConfigurationElement > fProcessFactories = null ;
440
451
452
+ /**
453
+ * List of exec factories.
454
+ */
455
+ private List <ExecFactoryFacade > execFactories ;
456
+
441
457
/**
442
458
* Service tracker for the workspace service
443
459
*/
@@ -981,7 +997,24 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
981
997
* @since 3.14
982
998
*/
983
999
public static Process exec (String [] cmdLine , File workingDirectory , String [] envp , boolean mergeOutput ) throws CoreException {
984
- Process p = null ;
1000
+ List <ExecFactoryFacade > factories = DebugPlugin .getDefault ().getExecFactories ();
1001
+ Optional <File > directory = shortenWindowsPath (workingDirectory );
1002
+ Optional <Map <String , String >> envMap = Optional .ofNullable (envp ).map (array -> {
1003
+ Map <String , String > map = new LinkedHashMap <>();
1004
+ for (String e : array ) {
1005
+ int index = e .indexOf ('=' );
1006
+ if (index != -1 ) {
1007
+ map .put (e .substring (0 , index ), e .substring (index + 1 ));
1008
+ }
1009
+ }
1010
+ return Map .copyOf (map );
1011
+ });
1012
+ for (ExecFactoryFacade holder : factories ) {
1013
+ Optional <Process > exec = holder .exec (cmdLine .clone (), directory , envMap , mergeOutput );
1014
+ if (exec .isPresent ()) {
1015
+ return exec .get ();
1016
+ }
1017
+ }
985
1018
try {
986
1019
// starting with and without merged output could be done with the
987
1020
// same process builder approach but since the handling of
@@ -990,25 +1023,18 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
990
1023
// builder to not break existing caller of this method
991
1024
if (mergeOutput ) {
992
1025
ProcessBuilder pb = new ProcessBuilder (cmdLine );
993
- if (workingDirectory != null ) {
994
- pb .directory (shortenWindowsPath (workingDirectory ));
995
- }
1026
+ directory .ifPresent (pb ::directory );
996
1027
pb .redirectErrorStream (mergeOutput );
997
- if (envp != null ) {
1028
+ if (envMap . isPresent () ) {
998
1029
Map <String , String > env = pb .environment ();
999
1030
env .clear ();
1000
- for (String e : envp ) {
1001
- int index = e .indexOf ('=' );
1002
- if (index != -1 ) {
1003
- env .put (e .substring (0 , index ), e .substring (index + 1 ));
1004
- }
1005
- }
1031
+ env .putAll (envMap .get ());
1006
1032
}
1007
- p = pb .start ();
1008
- } else if (workingDirectory == null ) {
1009
- p = Runtime .getRuntime ().exec (cmdLine , envp );
1033
+ return pb .start ();
1034
+ } else if (directory . isEmpty () ) {
1035
+ return Runtime .getRuntime ().exec (cmdLine , envp );
1010
1036
} else {
1011
- p = Runtime .getRuntime ().exec (cmdLine , envp , shortenWindowsPath ( workingDirectory ));
1037
+ return Runtime .getRuntime ().exec (cmdLine , envp , directory . get ( ));
1012
1038
}
1013
1039
} catch (IOException e ) {
1014
1040
Status status = new Status (IStatus .ERROR , getUniqueIdentifier (), ERROR , DebugCoreMessages .DebugPlugin_0 , e );
@@ -1021,18 +1047,18 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
1021
1047
if (handler != null ) {
1022
1048
Object result = handler .handleStatus (status , null );
1023
1049
if (result instanceof Boolean resultValue && resultValue ) {
1024
- p = exec (cmdLine , null );
1050
+ return exec (cmdLine , null );
1025
1051
}
1026
1052
}
1027
1053
}
1028
- return p ;
1054
+ return null ;
1029
1055
}
1030
1056
1031
1057
// https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
1032
1058
private static final int WINDOWS_MAX_PATH = 258 ;
1033
1059
1034
- private static File shortenWindowsPath (File path ) {
1035
- if (path .getPath ().length () > WINDOWS_MAX_PATH && Platform .OS .isWindows ()) {
1060
+ private static Optional < File > shortenWindowsPath (File path ) {
1061
+ if (path != null && path .getPath ().length () > WINDOWS_MAX_PATH && Platform .OS .isWindows ()) {
1036
1062
// When spawning new processes on Windows, there is no uniform way
1037
1063
// to use long working directory paths that exceed the default path
1038
1064
// length limit, like for example using the raw path prefix '\\?\'
@@ -1042,12 +1068,12 @@ private static File shortenWindowsPath(File path) {
1042
1068
@ SuppressWarnings ("restriction" )
1043
1069
String shortPath = org .eclipse .core .internal .filesystem .local .Win32Handler .getShortPathName (path .toString ());
1044
1070
if (shortPath != null ) {
1045
- return new File (shortPath );
1071
+ return Optional . of ( new File (shortPath ) );
1046
1072
} else {
1047
1073
log (Status .warning ("Working directory of process to create exceeds Window's MAX_PATH limit and shortening the path failed." )); //$NON-NLS-1$
1048
1074
}
1049
1075
}
1050
- return path ;
1076
+ return Optional . ofNullable ( path ) ;
1051
1077
}
1052
1078
1053
1079
/**
@@ -1189,6 +1215,39 @@ private void initializeProcessFactories() {
1189
1215
}
1190
1216
}
1191
1217
1218
+ private synchronized List <ExecFactoryFacade > getExecFactories () {
1219
+ if (execFactories == null ) {
1220
+ IExtensionPoint extensionPoint = Platform .getExtensionRegistry ().getExtensionPoint (DebugPlugin .PI_DEBUG_CORE , EXTENSION_POINT_EXEC_FACTORIES );
1221
+ IConfigurationElement [] infos = extensionPoint .getConfigurationElements ();
1222
+ List <ExecFactoryFacade > list = new ArrayList <>();
1223
+ for (IConfigurationElement configurationElement : infos ) {
1224
+ String clz = configurationElement .getAttribute ("class" ); //$NON-NLS-1$
1225
+ if (clz != null ) {
1226
+ int priority ;
1227
+ String attribute = configurationElement .getAttribute ("priority" ); //$NON-NLS-1$
1228
+ if (attribute == null ) {
1229
+ priority = 0 ;
1230
+ }
1231
+ try {
1232
+ priority = Integer .parseInt (attribute );
1233
+ } catch (NumberFormatException e ) {
1234
+ log (new Status (IStatus .ERROR , DebugPlugin .PI_DEBUG_CORE , ERROR , MessageFormat .format (DebugCoreMessages .DebugPlugin_invalid_exec_factory , new Object [] {
1235
+ configurationElement .getContributor ().getName () }), null ));
1236
+ priority = 0 ;
1237
+ }
1238
+ list .add (new ExecFactoryFacade (configurationElement , priority ));
1239
+ } else {
1240
+ String badDefiner = configurationElement .getContributor ().getName ();
1241
+ log (new Status (IStatus .ERROR , DebugPlugin .PI_DEBUG_CORE , ERROR , MessageFormat .format (DebugCoreMessages .DebugPlugin_invalid_exec_factory , new Object [] {
1242
+ badDefiner }), null ));
1243
+ }
1244
+ }
1245
+ list .sort (Comparator .comparingInt (ExecFactoryFacade ::getPriority ).reversed ());
1246
+ execFactories = List .copyOf (list );
1247
+ }
1248
+ return execFactories ;
1249
+ }
1250
+
1192
1251
private void invalidStatusHandler (Exception e , String id ) {
1193
1252
log (new Status (IStatus .ERROR , DebugPlugin .PI_DEBUG_CORE , ERROR , MessageFormat .format (DebugCoreMessages .DebugPlugin_5 , new Object [] { id }), e ));
1194
1253
}
@@ -1221,6 +1280,34 @@ public boolean equals(Object obj) {
1221
1280
}
1222
1281
}
1223
1282
1283
+ private class ExecFactoryFacade implements ExecFactory {
1284
+
1285
+ private IConfigurationElement element ;
1286
+ private int priority ;
1287
+
1288
+ ExecFactoryFacade (IConfigurationElement element , int priority ) {
1289
+ this .element = element ;
1290
+ this .priority = priority ;
1291
+ }
1292
+
1293
+ public int getPriority () {
1294
+ return priority ;
1295
+ }
1296
+
1297
+ @ Override
1298
+ public Optional <Process > exec (String [] cmdLine , Optional <File > workingDirectory , Optional <Map <String , String >> environment , boolean mergeOutput ) throws CoreException {
1299
+ ExecFactory extension ;
1300
+ try {
1301
+ extension = (ExecFactory ) element .createExecutableExtension (IConfigurationElementConstants .CLASS );
1302
+ } catch (CoreException e ) {
1303
+ log (e );
1304
+ return Optional .empty ();
1305
+ }
1306
+ return extension .exec (cmdLine , workingDirectory , environment , mergeOutput );
1307
+ }
1308
+
1309
+ }
1310
+
1224
1311
/**
1225
1312
* Executes runnables after event dispatch is complete.
1226
1313
*
0 commit comments