Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ java -jar glassfish-embedded-all.jar [--properties=FILE]
[--help] [applications or admin commands...]
```

```
```
Command line options with a value can be specified in two forms:
`--option=value` (e.g., `--httpPort=8090`) or `--option value` (e.g., `--httpPort 8090`).

`--properties=FILE`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,15 @@ public void addDeployable(String deployable) {
deployables.add(deployable);
}

void setOption(String key, String value) throws UnknownPropertyException {
/**
* Apply the option to this object.
* @param key Option name
* @param value Option value. Can be {@code null} if not value is provided.
* @return The {@code Option} that was used to apply the option. Can be {@code null}, e.g. for commands and deployable files.
* If the value was provided but it's not applicable, {@link Option#handlesValue(java.lang.String)} returns false, and the value should be treated as another option.
* @throws UnknownPropertyException If the option is not recognized and cannot be applied
*/
Option setOption(String key, String value) throws UnknownPropertyException {
try {
Option option;
try {
Expand All @@ -147,13 +155,15 @@ void setOption(String key, String value) throws UnknownPropertyException {
} else {
throw new UnknownPropertyException(key, value);
}
return;
return null;
}

option.handle(value, this);
return option;

} catch (RuntimeException e) {
logger.log(Level.WARNING, e, () -> "Could not set property " + key + " to value " + value + " - " + e.getMessage());
return null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,50 @@
*/
public class CommandLineParser {

private static Logger logger = Logger.getLogger(CommandLineParser.class.getName());
private static final Logger logger = Logger.getLogger(CommandLineParser.class.getName());

public Arguments parse(String[] commandLineArgs) {
Arguments arguments = new Arguments();
arguments.setDefaults();
for (int i = 0; i < commandLineArgs.length; i++) {
String arg = commandLineArgs[i];
if (arg.startsWith("-")) {
final int initialCharsToIgnore = arg.startsWith("--") ? 2 : 1;
String[] keyValue = arg.substring(2).split("=", initialCharsToIgnore);
int initialCharsToIgnore = arg.startsWith("--") ? 2 : 1;
String optionPart = arg.substring(initialCharsToIgnore);

String key;
String value;

// Use split with a limit of 2 to handle '=' in the value
String[] keyValue = optionPart.split("=", 2);
key = keyValue[0];

try {
boolean nextArgIsValue = false;
if (keyValue.length == 2) {
arguments.setOption(keyValue[0], keyValue[1]);
// Case 1: Handles --key=value or -k=value
value = keyValue[1];
} else {
arguments.setOption(keyValue[0], null); // No value, it's a flag
// Case 2: Handles --key value (space) OR --key (flag)

// Check if a "value" argument exists next
boolean hasNextArg = i + 1 < commandLineArgs.length;
// Check if the next arg is NOT an option itself
nextArgIsValue = hasNextArg && !commandLineArgs[i + 1].startsWith("-");

if (nextArgIsValue) {
// This is the --key value case
value = commandLineArgs[i + 1];
i++;
} else {
// This is a flag like --verbose
value = "true";
}
}

Option option = arguments.setOption(key, value);
if (nextArgIsValue && option != null && !option.handlesValue(value)) {
i--;
}
} catch (UnknownPropertyException e) {
logger.log(Level.WARNING, e, () -> "Unknown argument " + arg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ public void handle(String value, Arguments arguments) {
public void handle(String value, Arguments arguments) {
setPort(DEFAULT_HTTP_LISTENER, 0, arguments);
}

@Override
public boolean handlesValue(String value) {
return isBooleanValue(value);
}

},
AUTO_DEPLOY_DIR("autoDeployDir", "--autoDeployDir=DIRECTORY",
"Files and directories in this directory will be deployed as applications (in random order), as if they"
Expand Down Expand Up @@ -138,6 +144,11 @@ public void handle(String noInfo, Arguments arguments) {
arguments.noInfo = (noInfo == null || Boolean.valueOf(noInfo));
}

@Override
public boolean handlesValue(String value) {
return isBooleanValue(value);
}

},
SHUTDOWN("shutdown", Set.of("shut-down", "stop"), "--shut-down, --shutdown, --stop",
"Shut down GlassFish and the whole JVM process after server is started and initialized."
Expand All @@ -150,6 +161,12 @@ public void handle(String noInfo, Arguments arguments) {
public void handle(String value, Arguments arguments) {
arguments.shutdown = true;
}

@Override
public boolean handlesValue(String value) {
return isBooleanValue(value);
}

},
PROMPT("prompt", "--prompt",
"Run interactive prompt that allows running admin commands. This is useful in development"
Expand All @@ -158,12 +175,24 @@ public void handle(String value, Arguments arguments) {
public void handle(String value, Arguments arguments) {
arguments.prompt = true;
}

@Override
public boolean handlesValue(String value) {
return isBooleanValue(value);
}

},
HELP("help", "--help", "Print this help") {
@Override
public void handle(String value, Arguments arguments) {
arguments.askedForHelp = true;
}

@Override
public boolean handlesValue(String value) {
return isBooleanValue(value);
}

};

protected static final Logger logger = Logger.getLogger(Option.class.getName());
Expand Down Expand Up @@ -195,8 +224,28 @@ public static Option from(String key) throws NoSuchElementException {
}
}

/**
* Apply the option to the arguments structure.
* @param value Value for the option. Can be null if no value provided.
* @param arguments Output structure to modify according to the key and value
*/
public abstract void handle(String value, Arguments arguments);

/**
* Whether the value will be handled by this option or should be treated as another option.
* @param value Value of the option
* @return {@code true} if the value will be handled, either because a value is required, or because a value is optional and this value is valid
*/
public boolean handlesValue(String value) {
return true;
}

protected boolean isBooleanValue(String value) {
return value == null
|| "true".equalsIgnoreCase(value)
|| "false".equalsIgnoreCase(value);
}

public String getMainName() {
return mainName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ DESCRIPTION
OPTIONS
${OPTIONS}

Command line options with a value can be specified in two forms:
--option=value (e.g., --httpPort=8090) or --option value (e.g., --httpPort 8090).

Any argument that doesn't start with a hyphen (-), is treated as follows:

- If it's a file or directory, it's deployed at startup as an application.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2025 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.runnablejar.commandline;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.stream.Stream;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.of;

class CommandLineParserTest {

private PrintStream originalPrintStream;

@BeforeEach
void setup() {
originalPrintStream = System.err;
}

@AfterEach
void restore() {
System.setErr(originalPrintStream);
}

static Stream<org.junit.jupiter.params.provider.Arguments> portValues() {
return Stream.of(
of((Object) new String[]{"--port=9090"}),
of((Object) new String[]{"--port", "9090"})
);
}

@ParameterizedTest
@MethodSource("portValues")
void parsePortOption(String[] arguments) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
System.setErr(new PrintStream(out));
Arguments parse = new CommandLineParser().parse(arguments);
int port = parse.glassFishProperties.getPort("http-listener");
assertEquals(0, out.size(), "Expected no message to be printed");
assertEquals(9090, port);
}

static Stream<org.junit.jupiter.params.provider.Arguments> verboseValues() {
return Stream.of(
of((Object) new String[]{"--verbose"}),
of((Object) new String[]{"--verbose", "true"}),
of((Object) new String[]{"--verbose=true"})
);
}

@ParameterizedTest
@MethodSource("verboseValues")
void parseVerboseOption(String[] arguments) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
System.setErr(new PrintStream(out));
Arguments parse = new CommandLineParser().parse(arguments);
assertEquals(0, out.size(), "Expected no message to be printed");
assertEquals("true", parse.glassFishProperties.getProperty("verbose"));
}

@Test
void libraryAndWarOptionsTest() {
String[] arguments = {
"--noPort",
"--stop",
"add-library",
"testLibrary.jar",
"testLibraryApp.war"
};
Arguments parse = new CommandLineParser().parse(arguments);
String value = parse.glassFishProperties.getProperty("embedded-glassfish-config.server.network-config.network-listeners.network-listener.http-listener.enabled");
assertEquals("false", value);
assertEquals(3, parse.commands.size());
assertTrue(parse.shutdown);
}


}