OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 177 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 177 Rev 180
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
9
 * language governing permissions and limitations under the License.
10
 * 
10
 * 
11
 * When distributing the software, include this License Header Notice in each file.
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
12
 */
13
 
13
 
14
 package org.openconcerto.utils.prog;
14
 package org.openconcerto.utils.prog;
15
 
15
 
16
import org.openconcerto.utils.FileUtils;
16
import org.openconcerto.utils.FileUtils;
17
import org.openconcerto.utils.OSFamily;
17
import org.openconcerto.utils.OSFamily;
-
 
18
import org.openconcerto.utils.OSFamily.Unix;
18
import org.openconcerto.utils.ProcessStreams;
19
import org.openconcerto.utils.ProcessStreams;
19
import org.openconcerto.utils.PropertiesUtils;
20
import org.openconcerto.utils.PropertiesUtils;
20
 
21
 
21
import java.io.File;
22
import java.io.File;
22
import java.io.IOException;
23
import java.io.IOException;
23
import java.lang.ProcessBuilder.Redirect;
24
import java.lang.ProcessBuilder.Redirect;
24
import java.lang.management.ManagementFactory;
25
import java.lang.management.ManagementFactory;
25
import java.util.ArrayList;
26
import java.util.ArrayList;
26
import java.util.Arrays;
27
import java.util.Arrays;
27
import java.util.Collections;
28
import java.util.Collections;
28
import java.util.List;
29
import java.util.List;
29
import java.util.Map;
30
import java.util.Map;
30
import java.util.Properties;
31
import java.util.Properties;
31
import java.util.regex.Matcher;
32
import java.util.regex.Matcher;
32
import java.util.regex.Pattern;
33
import java.util.regex.Pattern;
33
 
34
 
34
/**
35
/**
35
 * A class meant to be used as the main class of a jar and which launch another instance of the Java
36
 * A class meant to be used as the main class of a jar and which launch another instance of the Java
36
 * VM.
37
 * VM.
37
 * 
38
 * 
38
 * @author Sylvain
39
 * @author Sylvain
39
 * @see #launch(String, List)
40
 * @see #launch(String, List)
40
 */
41
 */
41
public abstract class VMLauncher {
42
public abstract class VMLauncher {
42
 
43
 
-
 
44
    private static final String PROPERTIES_EXT = ".properties";
43
    /**
45
    /**
44
     * Boolean system property, if set to <code>true</code> then {@link #restart(Class, List)} will
46
     * Boolean system property, if set to <code>true</code> then {@link #restart(Class, List)} will
45
     * simply return <code>null</code>. Useful e.g. when using IDE launch configuration (to debug).
47
     * simply return <code>null</code>. Useful e.g. when using IDE launch configuration (to debug).
46
     */
48
     */
47
    static public final String NO_RESTART = "vm.noRestart";
49
    static public final String NO_RESTART = "vm.noRestart";
48
 
50
 
-
 
51
    // Explicitly passed to jpackage
-
 
52
    static public final String APPDIR_SYSPROP = "jpackage.app.dir";
-
 
53
 
-
 
54
    // Automatically set by the jpackage launcher (could be set explicitly using --java-options
-
 
55
    // '-Djpackage.app-path=$APPDIR/../../bin/launcher' if jpackage ever changes that)
-
 
56
    static public final String APP_EXE_SYSPROP = "jpackage.app-path";
-
 
57
 
-
 
58
    // The path to the app directory with the jar
-
 
59
    public static final File getJPackageAppDir() {
-
 
60
        final String appPath = System.getProperty(APPDIR_SYSPROP, "");
-
 
61
        return appPath.isEmpty() ? null : new File(appPath);
-
 
62
    }
-
 
63
 
-
 
64
    // The path to the executable
-
 
65
    private static final String getJPackageAppPath() {
-
 
66
        final String appPath = System.getProperty(APP_EXE_SYSPROP, "");
-
 
67
        return appPath.isEmpty() ? null : appPath;
-
 
68
    }
-
 
69
 
-
 
70
    private static final void addJPackageSystemPropertyArgument(final List<String> args, final String propName) {
-
 
71
        final String arg = getJPackageSystemPropertyArg(propName);
-
 
72
        if (arg != null)
-
 
73
            args.add(arg);
-
 
74
    }
-
 
75
 
-
 
76
    private static final String getJPackageSystemPropertyArg(final String propName) {
-
 
77
        final String val = System.getProperty(propName);
-
 
78
        if (val == null)
-
 
79
            return null;
-
 
80
        return "-D" + propName + "=" + val;
-
 
81
    }
-
 
82
 
49
    private static NativeLauncherFinder getNativeAppLauncher() {
83
    private static NativeLauncherFinder getNativeAppLauncher() {
50
        final OSFamily os = OSFamily.getInstance();
84
        final OSFamily os = OSFamily.getInstance();
51
        final NativeLauncherFinder l;
85
        final NativeLauncherFinder l;
52
        if (os.equals(OSFamily.Windows)) {
86
        if (os.equals(OSFamily.Windows)) {
53
            l = new WinLauncherFinder();
87
            l = new WinLauncherFinder();
54
        } else if (os.equals(OSFamily.Mac)) {
88
        } else if (os.equals(OSFamily.Mac)) {
55
            l = new MacLauncherFinder();
89
            l = new MacLauncherFinder();
-
 
90
        } else if (os instanceof Unix) {
-
 
91
            l = new UnixLauncherFinder();
56
        } else {
92
        } else {
57
            l = UnknownLauncherFinder;
93
            l = UnknownLauncherFinder;
58
        }
94
        }
59
        return l;
95
        return l;
60
    }
96
    }
61
 
97
 
62
    private static List<String> getNativeCommand(List<String> args) {
98
    private static List<String> getNativeCommand(List<String> args) {
63
        final NativeLauncherFinder l = getNativeAppLauncher();
99
        final NativeLauncherFinder l = getNativeAppLauncher();
64
        return l.getAppPath() == null ? null : l.getCommand(args);
100
        return l.getAppPath() == null ? null : l.getCommand(args);
65
    }
101
    }
66
 
102
 
67
    /**
103
    /**
68
     * Allow to find out if the running VM was launched using a native application.
104
     * Allow to find out if the running VM was launched using a native application.
69
     * 
105
     * 
70
     * @author Sylvain
106
     * @author Sylvain
71
     */
107
     */
72
    private static abstract class NativeLauncherFinder {
108
    private static abstract class NativeLauncherFinder {
73
        private final String cp, firstItem;
109
        private final String cp, firstItem;
74
 
110
 
75
        public NativeLauncherFinder() {
111
        public NativeLauncherFinder() {
76
            this.cp = ManagementFactory.getRuntimeMXBean().getClassPath();
112
            this.cp = ManagementFactory.getRuntimeMXBean().getClassPath();
77
            final int sepIndex = this.cp.indexOf(File.pathSeparatorChar);
113
            final int sepIndex = this.cp.indexOf(File.pathSeparatorChar);
78
            this.firstItem = sepIndex < 0 ? this.cp : this.cp.substring(0, sepIndex);
114
            this.firstItem = sepIndex < 0 ? this.cp : this.cp.substring(0, sepIndex);
79
        }
115
        }
80
 
116
 
81
        public final String getClassPath() {
117
        public final String getClassPath() {
82
            return this.cp;
118
            return this.cp;
83
        }
119
        }
84
 
120
 
85
        public final String getFirstItem() {
121
        public final String getFirstItem() {
86
            return this.firstItem;
122
            return this.firstItem;
87
        }
123
        }
88
 
124
 
89
        /**
125
        /**
90
         * The path to the native application if any.
126
         * The path to the native application if any.
91
         * 
127
         * 
92
         * @return the path, <code>null</code> if no native application could be found.
128
         * @return the path, <code>null</code> if no native application could be found.
93
         */
129
         */
94
        public abstract String getAppPath();
130
        public abstract String getAppPath();
95
 
131
 
96
        /**
132
        /**
97
         * The command to launch this application with the passed arguments.
133
         * The command to launch this application with the passed arguments.
98
         * 
134
         * 
99
         * @param args the program arguments.
135
         * @param args the program arguments.
100
         * @return the command.
136
         * @return the command.
-
 
137
         * @throws UnsupportedOperationException if {@link #getAppPath()} returns <code>null</code>.
101
         */
138
         */
-
 
139
        public final List<String> getCommand(final List<String> args) throws UnsupportedOperationException {
-
 
140
            final String appPath = this.getAppPath();
-
 
141
            if (appPath == null)
-
 
142
                throw new UnsupportedOperationException();
-
 
143
 
-
 
144
            return getCommand(appPath, args);
-
 
145
        }
-
 
146
 
102
        public abstract List<String> getCommand(final List<String> args);
147
        protected List<String> getCommand(final String appPath, final List<String> args) {
-
 
148
            final List<String> command = new ArrayList<String>(4 + args.size());
-
 
149
            command.add(appPath);
-
 
150
            command.addAll(args);
-
 
151
            return command;
-
 
152
        }
103
    }
153
    }
104
 
154
 
105
    private static class MacLauncherFinder extends NativeLauncherFinder {
155
    private static class MacLauncherFinder extends NativeLauncherFinder {
106
        private static final String APP_EXT = ".app";
156
        private static final String APP_EXT = ".app";
-
 
157
        // jpackage uses "Contents/app"
107
        private static final Pattern MAC_PATTERN = Pattern.compile(Pattern.quote(APP_EXT) + "/Contents/Resources(/Java)?/[^/]+\\.jar$");
158
        private static final Pattern MAC_PATTERN = Pattern.compile(Pattern.quote(APP_EXT) + "/Contents/(Resources(/Java)?|app)/[^/]+\\.jar$");
108
 
159
 
109
        @Override
160
        @Override
110
        public String getAppPath() {
161
        public String getAppPath() {
111
            final Matcher matcher = MAC_PATTERN.matcher(this.getFirstItem());
162
            final Matcher matcher = MAC_PATTERN.matcher(this.getFirstItem());
112
            if (matcher.matches()) {
163
            if (matcher.matches()) {
113
                final String appPath = getFirstItem().substring(0, matcher.start() + APP_EXT.length());
164
                final String appPath = getFirstItem().substring(0, matcher.start() + APP_EXT.length());
114
                final File contentsDir = new File(appPath, "Contents");
165
                final File contentsDir = new File(appPath, "Contents");
115
                final List<String> bundleContent = Arrays.asList(contentsDir.list());
-
 
116
                if (bundleContent.contains("Info.plist") && bundleContent.contains("PkgInfo") && new File(contentsDir, "MacOS").isDirectory())
166
                if (new File(contentsDir, "Info.plist").isFile() && new File(contentsDir, "MacOS").isDirectory())
117
                    return appPath;
167
                    return appPath;
118
            }
168
            }
119
            return null;
169
            return null;
120
        }
170
        }
121
 
171
 
122
        @Override
172
        @Override
123
        public List<String> getCommand(List<String> args) {
173
        protected List<String> getCommand(String appPath, List<String> args) {
124
            final List<String> command = new ArrayList<String>(4 + args.size());
174
            final List<String> command = new ArrayList<String>(4 + args.size());
125
            command.add("open");
175
            command.add("open");
126
            // since we restarting we need to launch a new instance of us
176
            // since we restarting we need to launch a new instance of us
127
            command.add("-n");
177
            command.add("-n");
128
            command.add(getAppPath());
178
            command.add(appPath);
129
            command.add("--args");
179
            command.add("--args");
130
            command.addAll(args);
180
            command.addAll(args);
131
            return command;
181
            return command;
132
        }
182
        }
133
    }
183
    }
134
 
184
 
135
    private static class WinLauncherFinder extends NativeLauncherFinder {
185
    private static class WinLauncherFinder extends NativeLauncherFinder {
136
        @Override
186
        @Override
137
        public String getAppPath() {
187
        public String getAppPath() {
138
            // launch4j
188
            // launch4j
139
            if (this.getFirstItem().endsWith(".exe"))
189
            if (this.getFirstItem().endsWith(".exe"))
140
                return getFirstItem();
190
                return getFirstItem();
141
            else
191
            else
142
                return null;
192
                return null;
143
        }
193
        }
-
 
194
    }
-
 
195
 
-
 
196
    private static class UnixLauncherFinder extends NativeLauncherFinder {
-
 
197
 
-
 
198
        private final String jpackageApp;
-
 
199
 
-
 
200
        public UnixLauncherFinder() {
-
 
201
            this.jpackageApp = getJPackageAppPath();
-
 
202
        }
144
 
203
 
145
        @Override
204
        @Override
146
        public List<String> getCommand(List<String> args) {
-
 
147
            final List<String> command = new ArrayList<String>(4 + args.size());
-
 
148
            command.add(getAppPath());
205
        public String getAppPath() {
149
            command.addAll(args);
-
 
150
            return command;
206
            return this.jpackageApp;
151
        }
207
        }
152
    }
208
    }
153
 
209
 
154
    private static final NativeLauncherFinder UnknownLauncherFinder = new NativeLauncherFinder() {
210
    private static final NativeLauncherFinder UnknownLauncherFinder = new NativeLauncherFinder() {
155
        @Override
211
        @Override
156
        public String getAppPath() {
212
        public String getAppPath() {
157
            return null;
213
            return null;
158
        }
214
        }
159
 
-
 
160
        @Override
-
 
161
        public List<String> getCommand(List<String> args) {
-
 
162
            throw new UnsupportedOperationException();
-
 
163
        }
-
 
164
    };
215
    };
165
 
216
 
166
    public static final Process restart(final Class<?> mainClass, final String... args) throws IOException {
217
    public static final Process restart(final Class<?> mainClass, final String... args) throws IOException {
167
        return restart(mainClass, Arrays.asList(args));
218
        return restart(mainClass, Arrays.asList(args));
168
    }
219
    }
169
 
220
 
170
    public static final Process restart(final Redirect action, final Class<?> mainClass, final String... args) throws IOException {
221
    public static final Process restart(final Redirect action, final Class<?> mainClass, final String... args) throws IOException {
171
        return restart(action, mainClass, Arrays.asList(args));
222
        return restart(action, mainClass, Arrays.asList(args));
172
    }
223
    }
173
 
224
 
174
    /**
225
    /**
175
     * Restart the VM. If this VM was launched using a native application (e.g. .exe or .app) then
226
     * Restart the VM. If this VM was launched using a native application (e.g. .exe or .app) then
176
     * this will be executed. Else the <code>mainClass</code> will be used.
227
     * this will be executed. Else the <code>mainClass</code> will be used.
177
     * 
228
     * 
178
     * @param mainClass the main() to use (if no native application was found).
229
     * @param mainClass the main() to use (if no native application was found).
179
     * @param args the program arguments to pass.
230
     * @param args the program arguments to pass.
180
     * @return the new process, <code>null</code> if the program wasn't started.
231
     * @return the new process, <code>null</code> if the program wasn't started.
181
     * @throws IOException if the VM couldn't be launched.
232
     * @throws IOException if the VM couldn't be launched.
182
     * @see #NO_RESTART
233
     * @see #NO_RESTART
183
     */
234
     */
184
    public static final Process restart(final Class<?> mainClass, final List<String> args) throws IOException {
235
    public static final Process restart(final Class<?> mainClass, final List<String> args) throws IOException {
185
        return restart(ProcessStreams.DISCARD, mainClass, args);
236
        return restart(ProcessStreams.DISCARD, mainClass, args);
186
    }
237
    }
187
 
238
 
188
    public static final Process restart(final Redirect action, final Class<?> mainClass, final List<String> args) throws IOException {
239
    public static final Process restart(final Redirect action, final Class<?> mainClass, final List<String> args) throws IOException {
189
        if (Boolean.getBoolean(NO_RESTART))
240
        if (Boolean.getBoolean(NO_RESTART))
190
            return null;
241
            return null;
191
        final File wd = FileUtils.getWD();
242
        final File wd = FileUtils.getWD();
192
        final List<String> command = getNativeCommand(args);
243
        final List<String> command = getNativeCommand(args);
193
        if (command != null) {
244
        if (command != null) {
194
            return new ProcessBuilder(command).directory(wd).redirectErrorStream(true).redirectOutput(action).start();
245
            return new ProcessBuilder(command).directory(wd).redirectErrorStream(true).redirectOutput(action).start();
195
        } else {
246
        } else {
196
            try {
247
            try {
197
                mainClass.getMethod("main", String[].class);
248
                mainClass.getMethod("main", String[].class);
198
            } catch (NoSuchMethodException e) {
249
            } catch (NoSuchMethodException e) {
199
                throw new IllegalArgumentException(mainClass + " doesn't containt a main()", e);
250
                throw new IllegalArgumentException(mainClass + " doesn't containt a main()", e);
200
            }
251
            }
201
            return new VMLauncher() {
252
            return new VMLauncher() {
202
                @Override
253
                @Override
203
                protected File getWD() {
254
                protected File getWD() {
204
                    return wd;
255
                    return wd;
205
                }
256
                }
206
 
257
 
207
                @Override
258
                @Override
208
                protected File getPropFile(String mainClass) {
259
                protected File getPropFile(String mainClass) {
209
                    return null;
260
                    return null;
210
                }
261
                }
211
 
262
 
212
                @Override
263
                @Override
213
                protected Redirect getStreamRedirect() {
264
                protected Redirect getStreamRedirect() {
214
                    return action;
265
                    return action;
215
                }
266
                }
216
            }.launch(mainClass.getName(), args);
267
            }.launch(mainClass.getName(), args);
217
        }
268
        }
218
    }
269
    }
219
 
270
 
220
    public static final String ENV_VMARGS = "JAVA_VMARGS";
271
    public static final String ENV_VMARGS = "JAVA_VMARGS";
221
    public static final String PROPS_VMARGS = "VMARGS";
272
    public static final String PROPS_VMARGS = "VMARGS";
222
    public static final String ENV_PROGARGS = "JAVA_PROGARGS";
273
    public static final String ENV_PROGARGS = "JAVA_PROGARGS";
223
 
274
 
-
 
275
    // Don't split on spaces to avoid dealing with quotes or escapes : vmArgs=-Dfoo bar\t-DotherProp
224
    // handle DOS, Mac and Unix newlines
276
    // Handle DOS, Mac and Unix newlines (and tabs).
225
    private static final Pattern NL = Pattern.compile("\\p{Cntrl}+");
277
    private static final Pattern NL = Pattern.compile("\\p{Cntrl}+");
226
 
278
 
227
    private File wd;
279
    private File wd;
228
 
280
 
229
    public VMLauncher() {
281
    public VMLauncher() {
230
        this.wd = null;
282
        this.wd = null;
231
    }
283
    }
232
 
284
 
233
    public final File getLauncherWD() {
285
    public final File getLauncherWD() {
234
        if (this.wd == null) {
286
        if (this.wd == null) {
-
 
287
            final File appDir = getJPackageAppDir();
-
 
288
            if (appDir == null) {
235
            final NativeLauncherFinder nativeAppLauncher = getNativeAppLauncher();
289
                final NativeLauncherFinder nativeAppLauncher = getNativeAppLauncher();
236
            final String appPath = nativeAppLauncher.getAppPath();
290
                final String appPath = nativeAppLauncher.getAppPath();
237
            if (appPath != null)
291
                if (appPath != null)
238
                this.wd = new File(appPath).getAbsoluteFile().getParentFile();
292
                    this.wd = new File(appPath).getAbsoluteFile().getParentFile();
239
            // when launched with -jar there's only one item
293
                // when launched with -jar there's only one item
240
            else if (nativeAppLauncher.getFirstItem().equals(nativeAppLauncher.getClassPath()) && new File(nativeAppLauncher.getFirstItem()).isFile())
294
                else if (nativeAppLauncher.getFirstItem().equals(nativeAppLauncher.getClassPath()) && new File(nativeAppLauncher.getFirstItem()).isFile())
241
                this.wd = new File(nativeAppLauncher.getFirstItem()).getParentFile();
295
                    this.wd = new File(nativeAppLauncher.getFirstItem()).getParentFile();
242
            // support launch in an IDE
296
                // support launch in an IDE
243
            else
297
                else
244
                this.wd = FileUtils.getWD();
298
                    this.wd = FileUtils.getWD();
-
 
299
            } else {
-
 
300
                this.wd = appDir;
-
 
301
            }
245
        }
302
        }
246
        return this.wd;
303
        return this.wd;
247
    }
304
    }
248
 
305
 
249
    private final List<String> split(String res) {
306
    private final List<String> split(String res) {
250
        res = res.trim();
307
        res = res.trim();
251
        if (res.length() == 0) {
308
        if (res.length() == 0) {
252
            return Collections.emptyList();
309
            return Collections.emptyList();
253
        } else {
310
        } else {
254
            return Arrays.asList(NL.split(res));
311
            return Arrays.asList(NL.split(res));
255
        }
312
        }
256
    }
313
    }
257
 
314
 
258
    private final List<String> getProp(final File propFile, final String propName) {
315
    private final List<String> getProp(final File propFile, final String propName) {
259
        return this.getProp(this.getProps(propFile), propName);
316
        return this.getProp(this.getProps(propFile), propName);
260
    }
317
    }
261
 
318
 
262
    private final Properties getProps(final File propFile) {
319
    private final Properties getProps(final File propFile) {
263
        if (propFile != null && propFile.canRead()) {
320
        if (propFile != null && propFile.canRead()) {
264
            try {
321
            try {
265
                return PropertiesUtils.createFromFile(propFile);
322
                return PropertiesUtils.createFromFile(propFile);
266
            } catch (IOException e) {
323
            } catch (IOException e) {
267
                e.printStackTrace();
324
                e.printStackTrace();
268
            }
325
            }
269
        }
326
        }
270
        return new Properties();
327
        return new Properties();
271
    }
328
    }
272
 
329
 
273
    private final List<String> getProp(final Properties props, final String propName) {
330
    private final List<String> getProp(final Properties props, final String propName) {
274
        String res = "";
331
        String res = "";
275
        if (props != null) {
332
        if (props != null) {
276
            res = props.getProperty(propName, res);
333
            res = props.getProperty(propName, res);
277
        }
334
        }
278
        return split(res);
335
        return split(res);
279
    }
336
    }
280
 
337
 
281
    public final Process launch(final String mainClass) throws IOException {
338
    public final Process launch(final String mainClass) throws IOException {
282
        return this.launch(mainClass, Collections.<String> emptyList());
339
        return this.launch(mainClass, Collections.<String> emptyList());
283
    }
340
    }
284
 
341
 
285
    /**
342
    /**
286
     * Launch a new Java VM. This method will try to launch {@link #getJavaBinary() java} from the
343
     * Launch a new Java VM. This method will try to launch {@link #getJavaBinary() java} from the
287
     * same installation, if that fails it will use the system path for binaries. VM arguments can
344
     * same installation, if that fails it will use the system path for binaries. VM arguments can
288
     * be specified with :
345
     * be specified with :
289
     * <ol>
346
     * <ol>
290
     * <li>the {@value #ENV_VMARGS} environment variable</li>
347
     * <li>the {@value #ENV_VMARGS} environment variable</li>
291
     * <li>the {@value #PROPS_VMARGS} property in {@link #getPropFile(String)}</li>
348
     * <li>the {@value #PROPS_VMARGS} property in {@link #getPropFile(String)}</li>
292
     * <li>the {@link #getVMArguments()} method</li>
349
     * <li>the {@link #getVMArguments()} method</li>
293
     * </ol>
350
     * </ol>
294
     * Program arguments :
351
     * Program arguments :
295
     * <ol>
352
     * <ol>
296
     * <li>the <code>progParams</code> parameter</li>
353
     * <li>the <code>progParams</code> parameter</li>
297
     * <li>the {@value #ENV_PROGARGS} environment variable</li>
354
     * <li>the {@value #ENV_PROGARGS} environment variable</li>
298
     * </ol>
355
     * </ol>
299
     * 
356
     * 
300
     * @param mainClass the main class.
357
     * @param mainClass the main class.
301
     * @param progParams the program arguments for <code>mainClass</code>.
358
     * @param progParams the program arguments for <code>mainClass</code>.
302
     * @return the new Process.
359
     * @return the new Process.
303
     * @throws IOException if the process couldn't be started.
360
     * @throws IOException if the process couldn't be started.
304
     */
361
     */
305
    public final Process launch(final String mainClass, final List<String> progParams) throws IOException {
362
    public final Process launch(final String mainClass, final List<String> progParams) throws IOException {
306
        final boolean debug = Boolean.getBoolean("launcher.debug");
363
        final boolean debug = Boolean.getBoolean("launcher.debug");
307
        final String javaBinary = getJavaBinary();
364
        final String javaBinary = getJavaBinary();
308
        final File sameJava = new File(System.getProperty("java.home"), "bin/" + javaBinary);
365
        final File sameJava = new File(System.getProperty("java.home"), "bin/" + javaBinary);
-
 
366
        // allow to know what binary (and thus java.home) was tested
-
 
367
        if (debug)
-
 
368
            System.err.println("sameJava : " + sameJava);
309
        final String java = sameJava.canExecute() ? sameJava.getAbsolutePath() : javaBinary;
369
        final String java = sameJava.canExecute() ? sameJava.getAbsolutePath() : javaBinary;
310
        final File propFile = this.getPropFile(mainClass);
370
        final File propFile = this.getPropFile(mainClass);
311
        final Properties props = this.getProps(propFile);
371
        final Properties props = this.getProps(propFile);
312
        if (debug)
372
        if (debug)
313
            System.err.println("propFile : " + propFile);
373
            System.err.println("propFile : " + propFile);
314
 
374
 
315
        final List<String> command = new ArrayList<String>();
375
        final List<String> command = new ArrayList<String>();
316
        command.add(java);
376
        command.add(java);
317
 
377
 
318
        if (this.enableRemoteDebug(props)) {
378
        if (this.enableRemoteDebug(props)) {
319
            command.add(RemoteDebugArgs.getArgs(props.getProperty("remoteDebugAddr")));
379
            command.add(RemoteDebugArgs.getArgs(props.getProperty("remoteDebugAddr")));
320
        }
380
        }
321
        command.addAll(this.getVMArguments());
381
        command.addAll(this.getVMArguments());
322
 
382
 
323
        // for java the last specified property wins
383
        // For java the last specified property wins. MAYBE concat properties whose names start with
-
 
384
        // PROPS_VMARGS, that way we could override just one of many e.g. VMARGS.garbageCollector.
324
        if (propFile != null) {
385
        if (propFile != null) {
325
            final List<String> appProps = this.getProp(props, PROPS_VMARGS);
386
            final List<String> appProps = this.getProp(props, PROPS_VMARGS);
326
            command.addAll(appProps);
387
            command.addAll(appProps);
-
 
388
            if (debug) {
-
 
389
                System.err.println("VM arguments from " + propFile + " : " + appProps);
-
 
390
            }
-
 
391
            // Don't use Properties(defaults) constructor since we want to combine values.
-
 
392
            final File localFile = FileUtils.prependSuffix(propFile, "-local", PROPERTIES_EXT);
327
            final File userFile = new File(System.getProperty("user.home"), ".java/ilm/" + propFile.getName());
393
            final File userFile = new File(System.getProperty("user.home"), ".java/ilm/" + propFile.getName());
-
 
394
            for (final File f : Arrays.asList(localFile, userFile)) {
328
            final List<String> userProps = this.getProp(userFile, PROPS_VMARGS);
395
                final List<String> moreProps = this.getProp(f, PROPS_VMARGS);
329
            command.addAll(userProps);
396
                command.addAll(moreProps);
330
            if (debug) {
397
                if (debug) {
331
                System.err.println("appProps : " + appProps);
398
                    System.err.println("VM arguments from " + f + " : " + moreProps);
332
                System.err.println("userProps ( from " + userFile + ") : " + userProps);
399
                }
333
            }
400
            }
334
        }
401
        }
335
        final String envVMArgs = System.getenv(ENV_VMARGS);
402
        final String envVMArgs = System.getenv(ENV_VMARGS);
336
        if (envVMArgs != null)
403
        if (envVMArgs != null)
337
            command.addAll(split(envVMArgs));
404
            command.addAll(split(envVMArgs));
-
 
405
        // launched app may also need this context
-
 
406
        addJPackageSystemPropertyArgument(command, APPDIR_SYSPROP);
-
 
407
        addJPackageSystemPropertyArgument(command, APP_EXE_SYSPROP);
338
 
408
 
339
        command.add("-cp");
409
        command.add("-cp");
340
        command.add(getClassPath());
410
        command.add(getClassPath());
341
        command.add(mainClass);
411
        command.add(mainClass);
342
        final String envProgArgs = System.getenv(ENV_PROGARGS);
412
        final String envProgArgs = System.getenv(ENV_PROGARGS);
343
        if (envProgArgs != null)
413
        if (envProgArgs != null)
344
            command.addAll(split(envProgArgs));
414
            command.addAll(split(envProgArgs));
345
        command.addAll(progParams);
415
        command.addAll(progParams);
346
 
416
 
347
        // inherit environment so that the next launch() can access the same variables
417
        // inherit environment so that the next launch() can access the same variables
348
        final ProcessBuilder procBuilder = new ProcessBuilder(command).directory(getWD());
418
        final ProcessBuilder procBuilder = new ProcessBuilder(command).directory(getWD());
349
        this.modifyEnv(procBuilder.environment());
419
        this.modifyEnv(procBuilder.environment());
350
        if (debug) {
420
        if (debug) {
351
            System.err.println("Command line : " + procBuilder.command());
421
            System.err.println("Command line : " + procBuilder.command());
352
            System.err.println("Dir : " + procBuilder.directory());
422
            System.err.println("Dir : " + procBuilder.directory());
353
            System.err.println("Std out and err :");
423
            System.err.println("Std out and err :");
354
        }
424
        }
355
 
425
 
356
        procBuilder.redirectErrorStream(true).redirectOutput(debug ? Redirect.INHERIT : this.getStreamRedirect());
426
        procBuilder.redirectErrorStream(true).redirectOutput(debug ? Redirect.INHERIT : this.getStreamRedirect());
357
        return procBuilder.start();
427
        return procBuilder.start();
358
    }
428
    }
359
 
429
 
360
    protected void modifyEnv(Map<String, String> environment) {
430
    protected void modifyEnv(Map<String, String> environment) {
361
    }
431
    }
362
 
432
 
363
    protected Redirect getStreamRedirect() {
433
    protected Redirect getStreamRedirect() {
364
        return ProcessStreams.DISCARD;
434
        return ProcessStreams.DISCARD;
365
    }
435
    }
366
 
436
 
367
    protected boolean enableRemoteDebug(Properties props) {
437
    protected boolean enableRemoteDebug(Properties props) {
368
        final String prop = props.getProperty("remoteDebug");
438
        final String prop = props.getProperty("remoteDebug");
369
        return prop == null ? remoteDebugDefault() : Boolean.parseBoolean(prop);
439
        return prop == null ? remoteDebugDefault() : Boolean.parseBoolean(prop);
370
    }
440
    }
371
 
441
 
372
    protected boolean remoteDebugDefault() {
442
    protected boolean remoteDebugDefault() {
373
        return false;
443
        return false;
374
    }
444
    }
375
 
445
 
376
    /**
446
    /**
377
     * The program to launch. This implementation returns <code>javaw</code> for Windows and
447
     * The program to launch. This implementation returns <code>javaw</code> for Windows and
378
     * <code>java</code> for other OS.
448
     * <code>java</code> for other OS.
379
     * 
449
     * 
380
     * @return the name of the binary.
450
     * @return the name of the binary.
381
     */
451
     */
382
    protected String getJavaBinary() {
452
    protected String getJavaBinary() {
383
        return OSFamily.getInstance() == OSFamily.Windows ? "javaw" : "java";
453
        return OSFamily.getInstance() == OSFamily.Windows ? "javaw" : "java";
384
    }
454
    }
385
 
455
 
386
    protected List<String> getVMArguments() {
456
    protected List<String> getVMArguments() {
387
        return Arrays.asList("-Dfile.encoding=UTF-8", "-Xms100M", "-Xmx256M");
457
        return Arrays.asList("-Dfile.encoding=UTF-8", "-Xms100M", "-Xmx256M");
388
    }
458
    }
389
 
459
 
390
    // by default in the same jar
460
    // by default in the same jar
391
    protected String getClassPath() {
461
    protected String getClassPath() {
392
        return ManagementFactory.getRuntimeMXBean().getClassPath();
462
        return ManagementFactory.getRuntimeMXBean().getClassPath();
393
    }
463
    }
394
 
464
 
395
    // by default in the same directory
465
    // by default in the same directory
396
    protected File getWD() {
466
    protected File getWD() {
397
        return this.getLauncherWD();
467
        return this.getLauncherWD();
398
    }
468
    }
399
 
469
 
400
    protected File getPropFile(final String mainClass) {
470
    protected File getPropFile(final String mainClass) {
401
        final String className = mainClass.substring(mainClass.lastIndexOf('.') + 1);
471
        final String className = mainClass.substring(mainClass.lastIndexOf('.') + 1);
402
        return new File(getWD(), className + ".properties");
472
        return new File(getWD(), className + PROPERTIES_EXT);
403
    }
473
    }
404
}
474
}