Skip to content

Commit

Permalink
Merge pull request #7139 from Viii3/FISH-10319-JVM-Option
Browse files Browse the repository at this point in the history
FISH-10319 Add create-jvm-option command
  • Loading branch information
Pandrex247 authored Jan 14, 2025
2 parents 0ba8e74 + a9ea98e commit d87a554
Show file tree
Hide file tree
Showing 7 changed files with 537 additions and 8 deletions.
3 changes: 2 additions & 1 deletion nucleus/core/kernel/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
holder.
-->
<!-- Portions Copyright [2016-2022] [Payara Foundation and/or its affiliates] -->
<!-- Portions Copyright [2016-2024] [Payara Foundation and/or its affiliates] -->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
Expand Down Expand Up @@ -99,6 +99,7 @@
<include>com/sun/enterprise/v3/**/statusNotDAS.html</include>
<include>com/sun/enterprise/v3/**/asynch-1F.gif</include>
<include>com/sun/enterprise/v3/**/backimage.jpg</include>
<include>fish/payara/enterprise/admin/**/*.properties</include>
</includes>
</resource>
<resource>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ static Config updateConfigIfNeeded(final Config initConfig,

}

static Config updateConfigIfNeeded(final Config initConfig,
final Target targetUtil,
final String target) {
public static Config updateConfigIfNeeded (final Config initConfig,
final Target targetUtil,
final String target) {
Config result = initConfig;
Config newConfig = targetUtil.getConfig(target);
if (newConfig!=null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.v3.admin.CLIUtil;
import com.sun.enterprise.v3.admin.CreateSystemProperties;
import jakarta.inject.Inject;
import org.glassfish.api.ActionReport;
import org.glassfish.api.I18n;
Expand Down Expand Up @@ -74,7 +73,7 @@
CommandTarget.CONFIG, CommandTarget.DAS, CommandTarget.DOMAIN, CommandTarget.STANDALONE_INSTANCE,CommandTarget.CLUSTERED_INSTANCE})
@I18n("create.system.property")
public class CreateSystemProperty implements AdminCommand, AdminCommandSecurity.Preauthorization, AdminCommandSecurity.AccessCheckProvider {
final private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(CreateSystemProperties.class);
final private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(CreateSystemProperty.class);

@Param(optional=true, defaultValue=SystemPropertyConstants.DAS_SERVER_NAME)
String target;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://github.com/payara/Payara/blob/main/LICENSE.txt
* See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* The Payara Foundation designates this particular file as subject to the "Classpath"
* exception as provided by the Payara Foundation in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package fish.payara.enterprise.admin.commands;

import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.JavaConfig;
import com.sun.enterprise.config.serverbeans.JvmOptionBag;
import com.sun.enterprise.universal.xml.MiniXmlParser;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.v3.admin.commands.CLIUtil;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.glassfish.api.ActionReport;
import org.glassfish.api.I18n;
import org.glassfish.api.Param;
import org.glassfish.api.UnknownOptionsAreOperands;
import org.glassfish.api.admin.AccessRequired;
import org.glassfish.api.admin.AdminCommand;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.api.admin.AdminCommandSecurity;
import org.glassfish.api.admin.ExecuteOn;
import org.glassfish.api.admin.RuntimeType;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.config.support.CommandTarget;
import org.glassfish.config.support.TargetType;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.internal.api.Target;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.config.ConfigSupport;
import org.jvnet.hk2.config.SingleConfigCode;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

@Service(name="create-jvm-option")
@PerLookup
@I18n("create.jvm.option")
@ExecuteOn({RuntimeType.DAS, RuntimeType.INSTANCE})
@TargetType({CommandTarget.DAS,CommandTarget.STANDALONE_INSTANCE,CommandTarget.CLUSTER,CommandTarget.CONFIG})
@UnknownOptionsAreOperands()
public final class CreateJvmOption implements AdminCommand, AdminCommandSecurity.Preauthorization {

@Param(name="target", optional=true, defaultValue = SystemPropertyConstants.DEFAULT_SERVER_INSTANCE_NAME)
String target;

@Param(name="profiler", optional=true)
Boolean addToProfiler=false;

@Param(name="assignment", primary=true)
String jvmAssignment;

@Param(name="min-jvm", optional = true)
String minJVM;
@Param(name="max-jvm", optional = true)
String maxJVM;

@Inject
Target targetService;

@Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
Config config;

private static final StringManager lsm = StringManager.getManager(CreateJvmOption.class);

@AccessRequired.To("update")
private JavaConfig javaConfig;

@Override
public boolean preAuthorization(AdminCommandContext context) {
config = CLIUtil.updateConfigIfNeeded(config, targetService, target);
javaConfig = config.getJavaConfig();
return true;
}


@Override
public void execute(AdminCommandContext context) {
final ActionReport report = context.getActionReport();
try {
JvmOptionBag bag;
if (addToProfiler) { //make sure profiler element exists before creating a JVM option for profiler
if (javaConfig.getProfiler() == null) {
report.setMessage(lsm.getString("create.profiler.first"));
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
return;
}
bag = javaConfig.getProfiler();
}
else {
bag = javaConfig;
}

ActionReport.MessagePart part = report.getTopMessagePart().addChild();
String validOption = (new MiniXmlParser.JvmOption(jvmAssignment)).option;
validate(bag, validOption, report);
validateHeapSize(bag, validOption, report);
add(bag, jvmAssignment, part);
}
catch (IllegalArgumentException iae) {
report.setMessage(iae.getMessage());
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
return;
}
catch (Exception e) {
String msg = e.getMessage() != null ? e.getMessage() :
lsm.getStringWithDefault("create.jvm.options.failed",
"Command: create-jvm-options failed", new String[]{e.getMessage()});
report.setMessage(msg);
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
report.setFailureCause(e);
return;
}
report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
}

private void validateHeapSize (JvmOptionBag bag, String option, ActionReport report) {
validateMax(bag, option, report);
validateMin(bag, option, report);
}

private void validateMax (JvmOptionBag bag, String option, ActionReport report) throws IllegalArgumentException {
if (!option.startsWith("-Xmx")) {
return;
}

//now, opt is something like -Xmx512m or -Xmx2g, or -Xmx=12 i.e. it may contain illegal characters
Pattern regex = Pattern.compile("-Xmx((\\d)+[m|g|k|M|G|K]?)+");
boolean matches = regex.matcher(option).matches();
if (!matches) {
String msg = lsm.getString("soft.invalid.xmx", option);
report.getTopMessagePart().addChild().setMessage(msg);
throw new IllegalArgumentException();
}


String existingXmx = bag.getStartingWith("-Xmx");
if (existingXmx != null) {
//maybe a different Xmx was given
String msg = lsm.getString("soft.xmx.exists.singular", existingXmx, option);
report.getTopMessagePart().addChild().setMessage(msg);
}

String existingXms = bag.getStartingWith("-Xms");
if (existingXms != null) {
int xmsInConfig = JvmOptionBag.Duck.toMeg(existingXms, "-Xms");
int xmxGiven = JvmOptionBag.Duck.toMeg(option, "-Xmx");
if (xmsInConfig > xmxGiven) { //i.e. domain.xml contains -Xms1g and you ask -Xmx512m to be set
String msg = lsm.getString("soft.xmx.smaller.than.xms", xmxGiven + " MB", xmsInConfig + " MB");
report.getTopMessagePart().addChild().setMessage(msg);
throw new IllegalArgumentException();
}
}
}

private void validateMin (JvmOptionBag bag, String option, ActionReport report) throws IllegalArgumentException {
if (!option.startsWith("-Xms")) {
return;
}

//now, opt is something like -Xms512m or -Xms2g, or -Xms=12 i.e. it may contain illegal characters
Pattern regex = Pattern.compile("-Xms((\\d)+[m|g|k|M|G|K]?)+");
boolean matches = regex.matcher(option).matches();
if (!matches) {
String msg = lsm.getString("soft.invalid.xms", option);
report.getTopMessagePart().addChild().setMessage(msg);
throw new IllegalArgumentException();
}

String existingXms = bag.getStartingWith("-Xms");
if (existingXms != null) {
String msg = lsm.getString("soft.xms.exists.singular", existingXms, option);
report.getTopMessagePart().addChild().setMessage(msg);
}
String existingXmx = bag.getStartingWith("-Xmx");
if (existingXmx != null) {
int xmxInConfig = JvmOptionBag.Duck.toMeg(existingXmx, "-Xmx");
int xmsGiven = JvmOptionBag.Duck.toMeg(option, "-Xms");
if (xmsGiven > xmxInConfig) { //i.e. domain.xml contains -Xms1g and you ask -Xmx512m to be set
String msg = lsm.getString("soft.xms.larger.than.xmx", xmsGiven + " MB", xmxInConfig + " MB");
report.getTopMessagePart().addChild().setMessage(msg);
throw new IllegalArgumentException();
}
}
}

/**
* Validates a JVM option that it is a valid option.
* <p>
* Valid options must either start with a {@code -} or <code>${ENV=}</code>
* @param bag Bag of JVM options that already exist
* @param option Option being added.
* @param report ignored
* @throws IllegalArgumentException If an invalid options is given
*/
private void validate(JvmOptionBag bag, String option, ActionReport report) throws IllegalArgumentException {
if (!option.startsWith("-") && !option.startsWith("${ENV=")) {
String msg = lsm.getString("joe.invalid.start", option);
throw new IllegalArgumentException(msg);
}
if (bag.contains(option)) {
// setting an option that already exists is considered an error
String msg = lsm.getString("joe.exists", option);
throw new IllegalArgumentException(msg);
}
}

private void add (final JvmOptionBag bag, final String option, final ActionReport.MessagePart part) throws Exception {
SingleConfigCode<JvmOptionBag> scc = bag1 -> {
List<String> unversionedCurrentOptions = bag1.getJvmOptions();

if (!unversionedCurrentOptions.contains(new MiniXmlParser.JvmOption(option).option)) {
List<String> jvmopts = new ArrayList<>(bag1.getJvmRawOptions());
if (option.startsWith("-Xms") && bag1.getStartingWith("-Xms") != null) {
jvmopts.removeIf(entry -> entry.startsWith("-Xms"));
}
else if (option.startsWith("-Xmx") && bag1.getStartingWith("-Xmx") != null) {
jvmopts.removeIf(entry -> entry.startsWith("-Xmx"));
}

jvmopts.add(
MiniXmlParser.JvmOption.hasVersionPattern(option) ?
new MiniXmlParser.JvmOption(option).toString() :
new MiniXmlParser.JvmOption(option, minJVM, maxJVM).toString()
);
bag1.setJvmOptions(jvmopts);
part.setMessage(lsm.getString("jvm.option.created", option));
}
else {
part.setMessage(lsm.getString("no.option.created"));
}
return true;
};
ConfigSupport.apply(scc, bag);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
#
# Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved.
#
# The contents of this file are subject to the terms of either the GNU
# General Public License Version 2 only ("GPL") or the Common Development
# and Distribution License("CDDL") (collectively, the "License"). You
# may not use this file except in compliance with the License. You can
# obtain a copy of the License at
# https://github.com/payara/Payara/blob/main/LICENSE.txt
# See the License for the specific
# language governing permissions and limitations under the License.
#
# When distributing the software, include this License Header Notice in each
# file and include the License file at glassfish/legal/LICENSE.txt.
#
# GPL Classpath Exception:
# The Payara Foundation designates this particular file as subject to the "Classpath"
# exception as provided by the Payara Foundation in the GPL Version 2 section of the License
# file that accompanied this code.
#
# Modifications:
# If applicable, add the following below the License Header, with the fields
# enclosed by brackets [] replaced by your own identifying information:
# "Portions Copyright [year] [name of copyright owner]"
#
# Contributor(s):
# If you wish your version of this file to be governed by only the CDDL or
# only the GPL Version 2, indicate your decision by adding "[Contributor]
# elects to include this software in this distribution under the [CDDL or GPL
# Version 2] license." If you don't indicate a single choice of license, a
# recipient has the option to distribute your version of this file under
# either the CDDL, the GPL Version 2 or to extend the choice of license to
# its licensees as provided above. However, if you add GPL Version 2 code
# and therefore, elected the GPL Version 2 license, then the option applies
# only if the new code is made subject to such option by the copyright
# holder.
#

create.profiler.first=No profiler configured yet. Create a profiler first.
joe.exists=JVM option {0} already exists in the configuration.
joe.invalid.start=JVM option {0} is invalid because it does not start with a ''-''
joe.invalid.cmd.syntax=The command line:{0} does not satisfy the syntax.\nIn a nutshell, all options should start with a ''-'', multiple options are separated by a '':''.\nA '':'' inside an option should be escaped with a ''\\''.\nSince shell interprets command arguments, make sure you quote it.\n
soft.invalid.xmx=It appears that given JVM option {0} represents invalid maximum heap for the JVM. Ensure that it is valid, by doing list-jvm-options.
soft.xmx.exists.singular=The previously configured maximum heap size ({0}) will be replaced by the new configuration: {1}
soft.xmx.smaller.than.xms=It appears that the maximum heap size specified: {0} is smaller than the minimum heap size in the configuration: {1}. JVM might not start. Ensure that this is valid, by doing doing list-jvm-options.
soft.invalid.xms=It appears that given JVM option {0} represents invalid initial heap for the JVM. Ensure that it is valid, by doing list-jvm-options.
soft.xms.exists.singular=The previously configured initial heap size ({0}) will be replaced by the new configuration: {1}
soft.xms.larger.than.xmx=It appears that the initial heap size specified: {0} is larger than the maximum heap size in the configuration: {1}. JVM might not start. Ensure that this is valid, by doing doing list-jvm-options.
jvm.option.created=Successfully added JVM option: {0}
no.option.created=No jvm-options were created (perhaps they were already present)
create.jvm.options.failed=Command: create-jvm-options failed. {0}
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ EXIT STATUS

SEE ALSO
delete-jvm-options(1), list-jvm-options(1), create-profiler(1),
restart-domain(1)
restart-domain(1), create-jvm-option(1)

asadmin(1M)

Expand All @@ -209,4 +209,4 @@ SEE ALSO
* Windows: java - the Java application launcher
(http://docs.oracle.com/javase/6/docs/technotes/tools/windows/java.html)

Java EE 8 21 Aug 2017 create-jvm-options(1)
Java EE 8 18 Dec 2024 create-jvm-options(1)
Loading

0 comments on commit d87a554

Please # to comment.