mapping = new HashMap<>();
+ Properties props = System.getProperties();
+ for (String propName : props.stringPropertyNames()) {
+ mapping.put(propName, props.getProperty(propName));
+ }
+
+ TokenResolver resolver = new TokenResolver(mapping);
+ String configWithTokensReplaced = resolver.resolve(writer.toString());
+ return new InputSource(new StringReader(configWithTokensReplaced));
+ }
+
+ private static File checkXMLFile(String xmlFullName) throws UserError {
+ try {
+ File f = new File(xmlFullName);
+ if (f.exists() && f.isFile() && f.canRead()) {
+ return f;
+ }
+ // If given file does not exists
+ xmlMessage(xmlFullName);
+ return null;
+ } catch (Exception ex) {
+ xmlMessage(xmlFullName);
+ return null;
+ }
+ }
+
+ private static void xmlMessage(String xmlFullName) throws UserError {
+ UserError ue = new UserError(MessageFormat.format(
+ "Client Container xml: {0} not found or unable to read.\nYou may want to use the -xml option to locate your configuration xml.",
+ xmlFullName));
+ ue.setUsage(getUsage());
+ throw ue;
+
+ }
+}
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientFacade.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientFacade.java
index b7c556eff58..214765beb7a 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientFacade.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientFacade.java
@@ -17,85 +17,14 @@
package org.glassfish.appclient.client;
-import com.sun.enterprise.container.common.spi.util.InjectionException;
-import com.sun.enterprise.deployment.node.SaxParserHandlerBundled;
-import com.sun.enterprise.universal.glassfish.TokenResolver;
-import com.sun.enterprise.util.LocalStringManager;
-import com.sun.enterprise.util.LocalStringManagerImpl;
-
-import jakarta.xml.bind.JAXBContext;
-import jakarta.xml.bind.JAXBException;
-import jakarta.xml.bind.Unmarshaller;
-import jakarta.xml.bind.ValidationEvent;
-import jakarta.xml.bind.util.ValidationEventCollector;
-
-import java.io.BufferedReader;
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringReader;
-import java.lang.instrument.Instrumentation;
-import java.lang.reflect.InvocationTargetException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.transform.sax.SAXSource;
-
-import org.glassfish.appclient.client.acc.ACCClassLoader;
-import org.glassfish.appclient.client.acc.ACCLogger;
-import org.glassfish.appclient.client.acc.AgentArguments;
-import org.glassfish.appclient.client.acc.AppClientContainer;
-import org.glassfish.appclient.client.acc.AppClientContainer.Builder;
-import org.glassfish.appclient.client.acc.AppclientCommandArguments;
-import org.glassfish.appclient.client.acc.CommandLaunchInfo;
-import org.glassfish.appclient.client.acc.CommandLaunchInfo.ClientLaunchType;
-import org.glassfish.appclient.client.acc.TargetServerHelper;
-import org.glassfish.appclient.client.acc.UserError;
-import org.glassfish.appclient.client.acc.Util;
-import org.glassfish.appclient.client.acc.config.AuthRealm;
-import org.glassfish.appclient.client.acc.config.ClientContainer;
-import org.glassfish.appclient.client.acc.config.ClientCredential;
-import org.glassfish.appclient.client.acc.config.MessageSecurityConfig;
-import org.glassfish.appclient.client.acc.config.Property;
-import org.glassfish.appclient.client.acc.config.TargetServer;
-import org.glassfish.common.util.GlassfishUrlClassLoader;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-
-import static org.glassfish.appclient.client.acc.CommandLaunchInfo.ClientLaunchType.UNKNOWN;
+import org.glassfish.embeddable.client.ApplicationClientClassLoader;
+import org.glassfish.embeddable.client.UserError;
/**
* @author tjquinn
*/
public class AppClientFacade {
- private static final String ACC_CONFIG_CONTENT_PROPERTY_NAME = "glassfish-acc.xml.content";
- private static final String MAN_PAGE_PATH = "/org/glassfish/appclient/client/acc/appclient.1m";
- private static final String LINE_SEP = System.lineSeparator();
-
- private static final Class> stringsAnchor = ACCClassLoader.class;
- private static LocalStringManager localStrings = new LocalStringManagerImpl(stringsAnchor);
-
- private static CommandLaunchInfo launchInfo;
- private static AppclientCommandArguments appClientCommandArgs;
- private static AppClientContainer appClientContainer;
-
/**
* Prepares the ACC (if not already done by the agent) and then transfers control to the ACC.
*
@@ -114,425 +43,14 @@ public class AppClientFacade {
*/
public static void main(String[] args) {
try {
- if (appClientContainer == null) {
- /*
- * The facade JAR has been run directly, not via the appclient script and not via Java Web Start. So we have no agent
- * arguments and no instrumentation for registering transformations.
- *
- * Because the agent has not run, we prepare the ACC here. (The agent would have done so itself had it run.)
- */
- prepareACC(null, null);
- }
-
- /*
- * In any case, the ACC is now prepared. Launch the app client in the prepared ACC.
- */
- appClientContainer.launch(args);
- } catch (Exception ex) {
- ex.printStackTrace();
- System.exit(1);
+ ApplicationClientClassLoader loader = (ApplicationClientClassLoader) Thread.currentThread()
+ .getContextClassLoader();
+ loader.getApplicationClientContainer().launch(args);
} catch (UserError ue) {
ue.displayAndExit();
- }
- }
-
- public static AppClientContainer acc() {
- return appClientContainer;
- }
-
- public static void launch(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException,
- IllegalArgumentException, InvocationTargetException, IOException, SAXException, InjectionException, UserError {
- appClientContainer.launch(args);
- }
-
- public static void prepareACC(String agentArgsText, Instrumentation inst) throws UserError, MalformedURLException, URISyntaxException,
- JAXBException, FileNotFoundException, ParserConfigurationException, SAXException, IOException, Exception {
- int version = Runtime.version().feature();
- if (version < 11) {
- throw new UserError(localStrings.getLocalString(stringsAnchor, "main.badVersion",
- "Current Java version {0} is too low; {1} or later required",
- new Object[] {System.getProperty("java.version"), "11"}));
- }
-
- /*
- * Analyze the agent argument string.
- */
- AgentArguments agentArgs = AgentArguments.newInstance(agentArgsText);
-
- /*
- * The agent arguments that correspond to the ones that we want to pass to the ACC are the ones with the "arg=" keyword
- * prefix. These will include arguments with meaning to the ACC (-textauth for example) as well as arguments to be
- * passed on to the client's main method.
- */
- appClientCommandArgs = AppclientCommandArguments.newInstance(agentArgs.namedValues("arg"));
-
- if (appClientCommandArgs.isUsage()) {
- usage(0);
- } else if (appClientCommandArgs.isHelp()) {
- help();
- }
-
- /*
- * Examine the agent arguments for settings about how to launch the client.
- */
- launchInfo = CommandLaunchInfo.newInstance(agentArgs);
- if (launchInfo.getClientLaunchType() == UNKNOWN) {
- usage(1);
- }
-
- /*
- * Handle the legacy env. variable APPCPATH.
- */
- ACCClassLoader loader = initClassLoader((inst == null));
- Thread.currentThread().setContextClassLoader(loader);
-
- /*
- * The installRoot property will be set by the ServerEnvironment initialization using the ACC start-up context. That
- * happens during the ACCModulesManager warm-up.
- */
-
- /*
- * Load the ACC configuration XML file.
- */
- ClientContainer clientContainer = readConfig(appClientCommandArgs.getConfigFilePath(), loader);
-
- /*
- * Decide what target servers to use. This combines any specified on the command line with any in the config file's
- * target-server elements as well as any set in the properties of the config file.
- */
- final TargetServer[] targetServers = TargetServerHelper.targetServers(clientContainer, appClientCommandArgs.getTargetServer());
-
- /*
- * Get the builder. Doing so correctly involves merging the configuration file data with some of the command line and
- * agent arguments.
- */
- final AppClientContainer.Builder builder = createBuilder(targetServers, clientContainer, appClientCommandArgs);
-
- /*
- * Create the ACC. Again, precisely how we create it depends on some of the command line arguments and agent arguments.
- */
- final AppClientContainer newACC = createContainer(builder, launchInfo, appClientCommandArgs);
-
- /*
- * Because the JMV might invoke the client's main class, the agent needs to prepare the container. (This is done as part
- * of the AppClientContainer.start() processing in the public API.
- */
- newACC.prepare(inst);
-
- appClientContainer = newACC;
- }
-
- private static void usage(final int exitStatus) {
- System.err.println(getUsage());
- System.exit(exitStatus);
- }
-
- private static void help() throws IOException {
- final InputStream is = AppClientFacade.class.getResourceAsStream(MAN_PAGE_PATH);
- if (is == null) {
- usage(0);
- }
-
- try (BufferedReader helpReader = new BufferedReader(new InputStreamReader(is))) {
- String line;
- while ((line = helpReader.readLine()) != null) {
- System.err.println(line);
- }
- } finally {
- System.exit(0);
- }
- }
-
- private static String getUsage() {
- return localStrings.getLocalString(stringsAnchor, "main.usage",
- "appclient [ | -client ] [-mainclass |-name ] [-xml ] [-textauth] [-user ] [-password |-passwordfile ] [-targetserver host[:port][,host[:port]...] [app-args]")
- + System.lineSeparator() + localStrings.getLocalString(stringsAnchor, "main.usage.1",
- " or :\n\tappclient [ ] [ | -jar ] [app args]");
- }
-
- private static ACCClassLoader initClassLoader(final boolean loaderShouldTransform) throws MalformedURLException {
- ACCClassLoader loader = ACCClassLoader.instance();
- if (loader == null) {
- loader = ACCClassLoader.newInstance(Thread.currentThread().getContextClassLoader(), loaderShouldTransform);
- }
-
- return loader;
- }
-
- private static Builder createBuilder(TargetServer[] targetServers, ClientContainer clientContainer,
- AppclientCommandArguments appClientCommandArgs) throws IOException {
- Builder builder = AppClientContainer.newBuilder(targetServers);
-
- /*
- * Augment the builder with settings from the app client options that can affect the builder itself. (This is distinct
- * from options that affect what client to launch which are handled in creating the ACC itself.
- */
- updateClientCredentials(builder, appClientCommandArgs);
- List msc = clientContainer.getMessageSecurityConfig();
- if (msc != null) {
- builder.getMessageSecurityConfig().addAll(clientContainer.getMessageSecurityConfig());
- }
-
- builder.logger(new ACCLogger(clientContainer.getLogService()));
-
- AuthRealm authRealm = clientContainer.getAuthRealm();
- if (authRealm != null) {
- builder.authRealm(authRealm.getClassname());
- }
-
- List property = clientContainer.getProperty();
- if (property != null) {
- builder.containerProperties(property);
- }
-
- return builder;
- }
-
- private static void updateClientCredentials(final Builder builder, final AppclientCommandArguments appClientCommandArgs) {
- ClientCredential clientCredential = builder.getClientCredential();
- String user = (clientCredential != null ? clientCredential.getUserName() : null);
- char[] pw = (clientCredential != null && clientCredential.getPassword() != null ? clientCredential.getPassword().get() : null);
-
- /*
- * user on command line?
- */
- String commandLineUser;
- if ((commandLineUser = appClientCommandArgs.getUser()) != null) {
- user = commandLineUser;
- }
-
- /*
- * password or passwordfile on command line? (theAppClientCommandArgs class takes care of reading the password from the
- * file and/or handling the -password option.
- */
- char[] commandLinePW;
- if ((commandLinePW = appClientCommandArgs.getPassword()) != null) {
- pw = commandLinePW;
- }
-
- builder.clientCredentials(user, pw);
- }
-
- private static AppClientContainer createContainer(Builder builder, CommandLaunchInfo launchInfo,
- AppclientCommandArguments appClientArgs) throws Exception, UserError {
-
- /*
- * The launchInfo already knows something about how to conduct the launch.
- */
- ClientLaunchType launchType = launchInfo.getClientLaunchType();
- AppClientContainer container;
-
- switch (launchType) {
- case JAR:
- case DIR:
- /*
- * The client name in the launch info is a file path for the directory or JAR to launch.
- */
- container = createContainerForAppClientArchiveOrDir(builder, launchInfo.getClientName(), appClientArgs.getMainclass(),
- appClientArgs.getName());
- break;
-
- case URL:
- container = createContainerForJWSLaunch(builder, launchInfo.getClientName(), appClientArgs.getMainclass(),
- appClientArgs.getName());
- break;
-
- case CLASS:
- container = createContainerForClassName(builder, launchInfo.getClientName());
- break;
-
- case CLASSFILE:
- container = createContainerForClassFile(builder, launchInfo.getClientName());
- break;
-
- default:
- container = null;
- }
-
- if (container == null) {
- throw new IllegalArgumentException("cannot choose app client launch type");
- }
-
- return container;
- }
-
- private static AppClientContainer createContainerForAppClientArchiveOrDir(Builder builder, String appClientPath, String mainClassName, String clientName) throws Exception, UserError {
- return builder.newContainer(Util.getURI(new File(appClientPath)), null /* callbackHandler */, mainClassName, clientName, appClientCommandArgs.isTextauth());
- }
-
- private static AppClientContainer createContainerForJWSLaunch(Builder builder, String appClientPath, String mainClassName, String clientName) throws Exception, UserError {
- return builder.newContainer(URI.create(appClientPath), null /* callbackHandler */, mainClassName, clientName);
- }
-
- private static AppClientContainer createContainerForClassName(Builder builder, String className) throws Exception, UserError {
-
- /*
- * Place "." on the class path so that when we convert the class file path to a fully-qualified class name and try to
- * load it, we'll find it.
- */
-
- ClassLoader loader = prepareLoaderToFindClassFile(Thread.currentThread().getContextClassLoader());
- Thread.currentThread().setContextClassLoader(loader);
-
- return builder.newContainer(Class.forName(className, true, loader));
- }
-
- private static ClassLoader prepareLoaderToFindClassFile(final ClassLoader currentLoader) throws MalformedURLException {
- File currentDirPath = new File(System.getProperty("user.dir"));
- ClassLoader newLoader = new GlassfishUrlClassLoader(new URL[] { currentDirPath.toURI().toURL() }, currentLoader);
- return newLoader;
- }
-
- private static AppClientContainer createContainerForClassFile(Builder builder, String classFilePath)
- throws MalformedURLException, ClassNotFoundException, FileNotFoundException, IOException, Exception, UserError {
-
- Util.verifyFilePath(classFilePath);
-
- /*
- * Strip off the trailing .class from the path and convert separator characters to dots to build a fully-qualified class
- * name.
- */
- String className = classFilePath.substring(0, classFilePath.lastIndexOf(".class")).replace(File.separatorChar, '.');
-
- return createContainerForClassName(builder, className);
- }
-
- private static ClientContainer readConfig(final String configPath, final ClassLoader loader) throws UserError, JAXBException,
- FileNotFoundException, ParserConfigurationException, SAXException, URISyntaxException, IOException {
- ClientContainer result = null;
- Reader configReader = null;
- String configFileLocationForErrorMessage = "";
- try {
- /*
- * During a Java Web Start launch, the config is passed as a property value.
- */
- String configInProperty = System.getProperty(ACC_CONFIG_CONTENT_PROPERTY_NAME);
- if (configInProperty != null) {
- /*
- * Awkwardly, the glassfish-acc.xml content refers to a config file. We work around this for Java Web Start launch by
- * capturing the content of that config file into a property setting in the generated JNLP document. We need to write
- * that content into a temporary file here on the client and then replace a placeholder in the glassfish-acc.xml content
- * with the path to that temp file.
- */
- final File securityConfigTempFile = Util.writeTextToTempFile(configInProperty, "wss-client-config", ".xml", false);
- final Properties p = new Properties();
- p.setProperty("security.config.path", securityConfigTempFile.getAbsolutePath());
- configInProperty = Util.replaceTokens(configInProperty, p);
- configReader = new StringReader(configInProperty);
- } else {
- /*
- * This is not a Java Web Start launch, so read the configuration from a disk file.
- */
- File configFile = checkXMLFile(configPath);
- checkXMLFile(appClientCommandArgs.getConfigFilePath());
- configReader = new FileReader(configFile);
- configFileLocationForErrorMessage = configFile.getAbsolutePath();
- }
-
- /*
- * Although JAXB makes it very simple to parse the XML into Java objects, we have to do several things explicitly to use
- * our local copies of DTDs and XSDs.
- */
- SAXParserFactory spf = SAXParserFactory.newInstance();
- spf.setValidating(true);
- spf.setNamespaceAware(true);
- SAXParser parser = spf.newSAXParser();
- XMLReader reader = parser.getXMLReader();
-
- /*
- * Get the local entity resolver that knows about the bundled .dtd and .xsd files.
- */
- reader.setEntityResolver(new SaxParserHandlerBundled());
-
- /*
- * To support installation-directory independence the default glassfish-acc.xml refers to the wss-config file using
- * ${com.sun.aas.installRoot}... So preprocess the glassfish-acc.xml file to replace any tokens with the corresponding
- * values, then submit that result to JAXB.
- */
- InputSource inputSource = replaceTokensForParsing(configReader);
-
- SAXSource saxSource = new SAXSource(reader, inputSource);
- JAXBContext jc = JAXBContext.newInstance(ClientContainer.class);
- final ValidationEventCollector vec = new ValidationEventCollector();
-
- Unmarshaller u = jc.createUnmarshaller();
- u.setEventHandler(vec);
- result = (ClientContainer) u.unmarshal(saxSource);
- if (vec.hasEvents()) {
- /*
- * The parser reported at least one warning or error. If all events were warnings, display them as a message and
- * continue. Otherwise there was at least one error or fatal, so say so and try to continue but say that such errors
- * might be fatal in future releases.
- */
- boolean isError = false;
- final StringBuilder sb = new StringBuilder();
- for (ValidationEvent ve : vec.getEvents()) {
- sb.append(ve.getMessage()).append(LINE_SEP);
- isError |= (ve.getSeverity() != ValidationEvent.WARNING);
- }
-
- String messageIntroduction = localStrings.getLocalString(AppClientFacade.class,
- isError ? "appclient.errParsingConfig" : "appclient.warnParsingConfig",
- isError ? "Error parsing app client container configuration {0}. Attempting to continue. In future releases such parsing errors might become fatal. Please correct your configuration file."
- : "Warning(s) parsing app client container configuration {0}. Continuing.",
- new Object[] { configFileLocationForErrorMessage });
-
- System.err.println(messageIntroduction + LINE_SEP + sb.toString());
- }
-
- return result;
- } finally {
- if (configReader != null) {
- configReader.close();
- }
- }
- }
-
- private static InputSource replaceTokensForParsing(final Reader reader) throws FileNotFoundException, IOException, URISyntaxException {
- char[] buffer = new char[1024];
-
- CharArrayWriter writer = new CharArrayWriter();
- int charsRead;
- while ((charsRead = reader.read(buffer)) != -1) {
- writer.write(buffer, 0, charsRead);
- }
- writer.close();
- reader.close();
-
- Map mapping = new HashMap<>();
- Properties props = System.getProperties();
- for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
- String propName = (String) e.nextElement();
- mapping.put(propName, props.getProperty(propName));
- }
-
- TokenResolver resolver = new TokenResolver(mapping);
- String configWithTokensReplaced = resolver.resolve(writer.toString());
- InputSource inputSource = new InputSource(new StringReader(configWithTokensReplaced));
- return inputSource;
- }
-
- private static File checkXMLFile(String xmlFullName) throws UserError {
- try {
- File f = new File(xmlFullName);
- if (f.exists() && f.isFile() && f.canRead()) {
- return f;
- } else {// If given file does not exists
- xmlMessage(xmlFullName);
- return null;
- }
} catch (Exception ex) {
- xmlMessage(xmlFullName);
- return null;
+ ex.printStackTrace();
+ System.exit(1);
}
}
-
- private static void xmlMessage(String xmlFullName) throws UserError {
- UserError ue = new UserError(localStrings.getLocalString(stringsAnchor, "main.cannot_read_clientContainer_xml",
- "Client Container xml: {0} not found or unable to read.\nYou may want to use the -xml option to locate your configuration xml.",
- new String[] { xmlFullName }));
- ue.setUsage(getUsage());
- throw ue;
-
- }
}
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientGroupFacade.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientGroupFacade.java
index f1038d151d8..3e6556028c3 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientGroupFacade.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/AppClientGroupFacade.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -16,11 +17,23 @@
package org.glassfish.appclient.client;
-import org.glassfish.appclient.client.acc.UserError;
+import org.glassfish.appclient.client.acc.AppClientContainer;
+import org.glassfish.embeddable.client.ApplicationClientClassLoader;
+import org.glassfish.embeddable.client.UserError;
/**
+ * The AppClientContainerAgent initializes instrumentation, class loaders and also
+ * the {@link AppClientContainer} instance, which must be reachable for this class then
+ * - and that was a problem. The solution is to "smuggle" the container using
+ * the class loader, which is the only thing which can be shared with the agent.
+ *
+ * With the usage of JPMS it got yet bit more complicated as we had to keep some order
+ * between classes and class loaders and we also noticed an issue with Windows which limit
+ * the length of the command line; therefore we introduced another layer which automatically
+ * detects some GlassFish's jar files and we don't need to add them to the -classpath argument.
*
* @author tjquinn
+ * @author David Matejcek
*/
public class AppClientGroupFacade {
@@ -29,19 +42,14 @@ public class AppClientGroupFacade {
*/
public static void main(String[] args) {
try {
- if (AppClientFacade.acc() == null) {
- /*
- * The facade JAR has been run directly, not via the appclient script and not via Java Web Start. So we have no agent
- * arguments and no instrumentation for registering transformations.
- */
- AppClientFacade.prepareACC(null, null);
- }
- AppClientFacade.launch(args);
+ ApplicationClientClassLoader loader = (ApplicationClientClassLoader) Thread.currentThread()
+ .getContextClassLoader();
+ loader.getApplicationClientContainer().launch(args);
+ } catch (UserError ue) {
+ ue.displayAndExit();
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
- } catch (UserError ue) {
- ue.displayAndExit();
}
}
}
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSACCMaskingClassLoader.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSACCMaskingClassLoader.java
index b2b9e521c97..5e4368c827a 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSACCMaskingClassLoader.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSACCMaskingClassLoader.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation
* Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -42,7 +43,7 @@ class JWSACCMaskingClassLoader extends MaskingClassLoader {
private final Collection endorsedPackagesToMask;
JWSACCMaskingClassLoader(ClassLoader parent, Collection endorsedPackagesToMask) {
- super(parent, Collections.EMPTY_SET /* punchins */, Collections.EMPTY_SET /* multiples */,
+ super(parent, Collections.emptySet() /* punchins */, Collections.emptySet() /* multiples */,
false /* useExplicitCallsToFindSystemClass */);
this.endorsedPackagesToMask = endorsedPackagesToMask;
}
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSAppClientContainerMain.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSAppClientContainerMain.java
index 2cbfa87b1dc..8845e183568 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSAppClientContainerMain.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/JWSAppClientContainerMain.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -33,9 +34,9 @@
import javax.swing.SwingUtilities;
-import org.glassfish.appclient.client.acc.UserError;
import org.glassfish.appclient.client.jws.boot.ErrorDisplayDialog;
import org.glassfish.appclient.client.jws.boot.LaunchSecurityHelper;
+import org.glassfish.embeddable.client.UserError;
/**
*
@@ -88,18 +89,14 @@ public static void main(String[] args) {
try {
now = System.currentTimeMillis();
- /*
- * Process any arguments (conveyed as properties in the JNLP) intended for the JWS-aware ACC.
- */
+ // Process any arguments (conveyed as properties in the JNLP) intended for the JWS-aware ACC.
processJWSArgs();
final String agentArgsText = System.getProperty("agent.args");
LaunchSecurityHelper.setPermissions();
- /*
- * Prevent the Java Web Start class loader from delegating to its parent when resolving classes and resources that
- * should come from the GlassFish-provided endorsed JARs.
- */
+ // Prevent the Java Web Start class loader from delegating to its parent when resolving
+ // classes and resources that should come from the GlassFish-provided endorsed JARs.
insertMaskingLoader();
final ClientRunner runner = new ClientRunner(agentArgsText, args);
@@ -133,8 +130,8 @@ private ClientRunner(final String agentArgsText, final String[] args) {
@Override
public void run() {
try {
- AppClientFacade.prepareACC(agentArgsText, null);
- AppClientFacade.launch(args);
+ AppClientContainerHolder.init(agentArgsText, null);
+ AppClientContainerHolder.getInstance().launch(args);
logger.log(Level.FINE, "JWSAppClientContainer finished after {0} ms", (System.currentTimeMillis() - now));
} catch (UserError ue) {
if (!isTestMode()) {
@@ -155,9 +152,7 @@ private static void insertMaskingLoader() throws IOException, NoSuchFieldExcepti
props.load(sr);
final ClassLoader jwsLoader = Thread.currentThread().getContextClassLoader();
-
final ClassLoader mcl = getMaskingClassLoader(jwsLoader.getParent(), props);
-
final Field jwsLoaderParentField = ClassLoader.class.getDeclaredField("parent");
jwsLoaderParentField.setAccessible(true);
jwsLoaderParentField.set(jwsLoader, mcl);
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/ACCStartupContext.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/ACCStartupContext.java
index e6e4e6e77a1..b9b4cf304b4 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/ACCStartupContext.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/ACCStartupContext.java
@@ -17,7 +17,6 @@
package org.glassfish.appclient.client.acc;
-import com.sun.enterprise.glassfish.bootstrap.cfg.AsenvConf;
import com.sun.enterprise.module.bootstrap.StartupContext;
import com.sun.enterprise.util.io.FileUtils;
@@ -26,10 +25,20 @@
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Map;
import java.util.Properties;
+import org.glassfish.main.jdke.props.EnvToPropsConverter;
import org.jvnet.hk2.annotations.Service;
+import static com.sun.enterprise.util.SystemPropertyConstants.AGENT_ROOT_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.CONFIG_ROOT_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.DERBY_ROOT_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.DOMAINS_ROOT_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.IMQ_BIN_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.IMQ_LIB_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.INSTALL_ROOT_PROPERTY;
+import static com.sun.enterprise.util.SystemPropertyConstants.JAVA_ROOT_PROPERTY_ASENV;
/**
* Start-up context for the ACC. Note that this context is used also for
* Java Web Start launches.
@@ -40,8 +49,6 @@
@Singleton
public class ACCStartupContext extends StartupContext {
- private static final String DERBY_ROOT_PROPERTY = "AS_DERBY_INSTALL";
-
public ACCStartupContext() {
super(accEnvironment());
}
@@ -53,13 +60,20 @@ public ACCStartupContext() {
* @return
*/
private static Properties accEnvironment() {
- final Properties environment = AsenvConf.parseAsEnv(getRootDirectory()).toProperties();
- environment.setProperty("com.sun.aas.installRoot", getRootDirectory().getAbsolutePath());
- final File javadbDir = new File(getRootDirectory().getParentFile(), "javadb");
- if (javadbDir.isDirectory()) {
- environment.setProperty(DERBY_ROOT_PROPERTY, javadbDir.getAbsolutePath());
- }
- return environment;
+ final File rootDirectory = getRootDirectory();
+ final Map pairs = Map.of(
+ "AS_DERBY_INSTALL", DERBY_ROOT_PROPERTY,
+ "AS_IMQ_LIB", IMQ_LIB_PROPERTY,
+ "AS_IMQ_BIN", IMQ_BIN_PROPERTY,
+ "AS_CONFIG", CONFIG_ROOT_PROPERTY,
+ "AS_INSTALL", INSTALL_ROOT_PROPERTY,
+ "AS_JAVA", JAVA_ROOT_PROPERTY_ASENV,
+ "AS_DEF_DOMAINS_PATH", DOMAINS_ROOT_PROPERTY,
+ "AS_DEF_NODES_PATH", AGENT_ROOT_PROPERTY);
+ Map files = new EnvToPropsConverter(rootDirectory.toPath()).convert(pairs);
+ Properties env = new Properties();
+ files.entrySet().forEach(e -> env.put(e.getKey(), e.getValue().getPath()));
+ return env;
}
private static File getRootDirectory() {
@@ -69,7 +83,7 @@ private static File getRootDirectory() {
*/
URI jarURI = null;
try {
- jarURI = ACCClassLoader.class.getProtectionDomain().getCodeSource().getLocation().toURI();
+ jarURI = ACCStartupContext.class.getProtectionDomain().getCodeSource().getLocation().toURI();
} catch (URISyntaxException ex) {
throw new RuntimeException(ex);
}
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AgentArguments.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AgentArguments.java
index a49e078f102..d9fc18e50d8 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AgentArguments.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AgentArguments.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -23,7 +24,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.glassfish.appclient.client.CLIBootstrap;
+import org.glassfish.embeddable.client.ApplicationClientCLIEncoding;
import static java.util.Collections.emptyList;
import static java.util.regex.Pattern.DOTALL;
@@ -103,10 +104,10 @@ private void scan(String args) {
Matcher agentArgMatcher = agentArgPattern.matcher(args);
while (agentArgMatcher.find()) {
String keyword = agentArgMatcher.group(KEYWORD);
- /*
- * Either the quoted string group or the unquoted string group from the matcher will be valid.
- */
- String value = CLIBootstrap.decodeArg(agentArgMatcher.group(QUOTED) != null ? agentArgMatcher.group(QUOTED) : agentArgMatcher.group(UNQUOTED));
+ // Either the quoted string group or the unquoted string group from the matcher will be valid.
+ String value = ApplicationClientCLIEncoding.decodeArg(agentArgMatcher.group(QUOTED) == null
+ ? agentArgMatcher.group(UNQUOTED)
+ : agentArgMatcher.group(QUOTED));
values.computeIfAbsent(keyword, e -> new ArrayList<>()).add(value);
}
diff --git a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AppClientContainer.java b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AppClientContainer.java
index b7f416f5efd..071b4ff6645 100644
--- a/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AppClientContainer.java
+++ b/appserver/appclient/client/acc/src/main/java/org/glassfish/appclient/client/acc/AppClientContainer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation.
+ * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation.
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -40,7 +40,8 @@
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URL;
-import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
@@ -63,6 +64,8 @@
import org.glassfish.appclient.client.acc.config.Property;
import org.glassfish.appclient.client.acc.config.Security;
import org.glassfish.appclient.client.acc.config.TargetServer;
+import org.glassfish.embeddable.client.ApplicationClientContainer;
+import org.glassfish.embeddable.client.UserError;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
@@ -79,7 +82,7 @@
* create a new builder for an ACC (see {@link #newBuilder} and {@link AppClientContainerBuilder}),
* optionally modify the configuration by invoking various builder methods,
* create an embedded instance of the ACC from the builder using {@link AppClientContainerBuilder#newContainer() },
- * startClient the client using {@link #startClient(String[])}, and
+ * startClient the client using {@link #launch(String[])}, and
* stop the container using {@link #stop()}.
*
*
@@ -166,7 +169,7 @@
*/
@Service
@PerLookup
-public class AppClientContainer {
+public class AppClientContainer implements ApplicationClientContainer {
// XXX move this
/** Prop name for keeping temporary files */
@@ -194,20 +197,16 @@ public class AppClientContainer {
@Inject
private ServiceLocator habitat;
- private Builder builder;
-
private Cleanup cleanup;
- private State state = State.INSTANTIATED; // HK2 will create the instance
+ private volatile State state;
private ClientMainClassSetting clientMainClassSetting;
- private URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
+ private TransformingClassLoader classLoader;
private Launchable client;
- private CallbackHandler callerSuppliedCallbackHandler;
-
/** returned from binding the app client to naming; used in preparing component invocation */
private String componentId;
@@ -221,54 +220,32 @@ public static AppClientContainer.Builder newBuilder(final TargetServer[] targetS
return new AppClientContainerBuilder(targetServers);
}
- /*
- * ********************* ABOUT INITIALIZATION ********************
- *
- * Note that, internally, the AppClientContainerBuilder's newContainer methods use HK2 to instantiate the
- * AppClientContainer object (so we can inject references to various other services).
- *
- * The newContainer method then invokes one of the ACC's prepare
methods to initialize the ACC fully. All
- * that is left at that point is for the client's main method to be invoked.
- *
- */
- public void startClient(String[] args) throws Exception, UserError {
- prepare(null);
- launch(args);
+ private AppClientContainer() {
+ this.classLoader = (TransformingClassLoader) Thread.currentThread().getContextClassLoader();
+ this.state = State.INSTANTIATED;
}
void prepareSecurity(final TargetServer[] targetServers, final List msgSecConfigs,
final Properties containerProperties, final ClientCredential clientCredential,
- final CallbackHandler callerSuppliedCallbackHandler, final URLClassLoader classLoader, final boolean isTextAuth)
+ final CallbackHandler callerSuppliedCallbackHandler, final boolean isTextAuth)
throws ReflectiveOperationException, InjectionException, IOException, SAXException {
- appClientContainerSecurityHelper.init(targetServers, msgSecConfigs, containerProperties, clientCredential, callerSuppliedCallbackHandler, classLoader,
- client.getDescriptor(classLoader), isTextAuth);
- }
-
- void setCallbackHandler(final CallbackHandler callerSuppliedCallbackHandler) {
- this.callerSuppliedCallbackHandler = callerSuppliedCallbackHandler;
- }
-
- void setBuilder(final Builder builder) {
- this.builder = builder;
+ appClientContainerSecurityHelper.init(targetServers, msgSecConfigs, containerProperties, clientCredential,
+ callerSuppliedCallbackHandler, classLoader, client.getDescriptor(classLoader), isTextAuth);
}
- public void prepare(final Instrumentation inst) throws NamingException, IOException, InstantiationException, IllegalAccessException,
- InjectionException, ClassNotFoundException, SAXException, NoSuchMethodException, UserError {
+ public void prepare(final Instrumentation inst) throws Exception, UserError {
completePreparation(inst);
}
void setClient(final Launchable client) throws ClassNotFoundException {
this.client = client;
clientMainClassSetting = ClientMainClassSetting.set(client.getMainClass());
-
}
void processPermissions() throws IOException {
// need to process the permissions files
- if (classLoader instanceof ACCClassLoader) {
- ((ACCClassLoader) classLoader).processDeclaredPermissions();
- }
+ classLoader.processDeclaredPermissions();
}
protected Class> loadClass(final String className) throws ClassNotFoundException {
@@ -281,44 +258,37 @@ protected ClassLoader getClassLoader() {
/**
* Gets the ACC ready so the main class can run. This can be followed, immediately or after some time, by either an
- * invocation of {@link #launch(java.lang.String[]) or by the JVM invoking the client's main method (as would happen
+ * invocation of {@link #launch(java.lang.String[])}
+ * @throws Exception or by the JVM invoking the client's main method (as would happen
* during a java -jar theClient.jar
launch.
- *
- * @throws java.lang.Exception
+ * @throws UserError
*/
- private void completePreparation(final Instrumentation inst) throws NamingException, IOException, InstantiationException,
- IllegalAccessException, InjectionException, ClassNotFoundException, SAXException, NoSuchMethodException, UserError {
+ private void completePreparation(final Instrumentation inst) throws Exception, UserError {
if (state != State.INSTANTIATED) {
- throw new IllegalStateException();
+ throw new IllegalStateException(
+ "Expected state was " + State.INSTANTIATED + ", but current state was " + state);
}
- /*
- * Attach any names defined in the app client. Validate the descriptor first, then use it to bind names in the app
- * client. This order is important - for example, to set up message destination refs correctly.
- */
+ // Attach any names defined in the app client. Validate the descriptor first, then use it to
+ // bind names in the app client. This order is important - for example, to set up message
+ // destination refs correctly.
client.validateDescriptor();
final ApplicationClientDescriptor desc = client.getDescriptor(classLoader);
componentId = componentEnvManager.bindToComponentNamespace(desc);
- /*
- * Arrange for cleanup now instead of during launch() because in some use cases the JVM will invoke the client's main
- * method itself and launch will be skipped.
- */
+ // Arrange for cleanup now instead of during launch() because in some use cases the JVM will
+ // invoke the client's main method itself and launch will be skipped.
cleanup = Cleanup.arrangeForShutdownCleanup(logger, habitat, desc);
- /*
- * Allow pre-destroy handling to work on the main class during clean-up.
- */
+ // Allow pre-destroy handling to work on the main class during clean-up.
cleanup.setInjectionManager(injectionManager, ClientMainClassSetting.clientMainClass);
- /*
- * If this app client contains persistence unit refs, then initialize the PU handling.
- */
+ // If this app client contains persistence unit refs, then initialize the PU handling.
Collection extends PersistenceUnitDescriptor> referencedPUs = desc.findReferencedPUs();
if (referencedPUs != null && !referencedPUs.isEmpty()) {
- ProviderContainerContractInfoImpl pcci = new ProviderContainerContractInfoImpl((ACCClassLoader) getClassLoader(), inst,
- client.getAnchorDir(), connectorRuntime);
+ ProviderContainerContractInfoImpl pcci = new ProviderContainerContractInfoImpl(
+ (TransformingClassLoader) getClassLoader(), inst, client.getAnchorDir(), connectorRuntime);
for (PersistenceUnitDescriptor puDesc : referencedPUs) {
PersistenceUnitLoader pul = new PersistenceUnitLoader(puDesc, pcci);
desc.addEntityManagerFactory(puDesc.getName(), pul.getEMF());
@@ -340,47 +310,45 @@ private void completePreparation(final Instrumentation inst) throws NamingExcept
managedBeanManager.loadManagedBeans(desc.getApplication());
cleanup.setManagedBeanManager(managedBeanManager);
- /**
- * We don't really need the main method here but we do need the side-effects.
- */
+ // We don't really need the main method here but we do need the side-effects.
getMainMethod();
state = State.PREPARED;
}
- public void launch(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException,
- IllegalArgumentException, InvocationTargetException, IOException, SAXException, InjectionException, UserError {
+ public void launch(String[] args) throws UserError {
if (state != State.PREPARED) {
- throw new IllegalStateException();
- }
- Method mainMethod = getMainMethod();
- // build args to the main and call it
- Object params[] = new Object[1];
- params[0] = args;
-
- if (logger.isLoggable(Level.FINE)) {
- dumpLoaderURLs();
+ throw new IllegalStateException("Unexpected state. Expected " + State.PREPARED + ", actual is " + state);
+ }
+ Thread.currentThread().setContextClassLoader(classLoader);
+ try {
+ Method mainMethod = getMainMethod();
+ // build args to the main and call it
+ if (logger.isLoggable(Level.FINE)) {
+ logger.log(Level.FINE,
+ "Current thread's classloader: " + Thread.currentThread().getContextClassLoader());
+ }
+ mainMethod.invoke(null, new Object[] {args});
+ state = State.STARTED;
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof UserError) {
+ throw (UserError) e.getCause();
+ }
+ throw new IllegalStateException("Launch failed.", e.getCause());
+ } catch (Exception e) {
+ throw new IllegalStateException("Launch failed.", e);
+ } finally {
+ // We need to clean up when the EDT ends or, if there is no EDT, right away.
+ // In particular, JMS/MQ-related non-daemon threads might still be running due to open
+ // queueing connections.
+ cleanupWhenSafe();
}
- mainMethod.invoke(null, params);
- state = State.STARTED;
-
- /*
- * We need to clean up when the EDT ends or, if there is no EDT, right away. In particular, JMS/MQ-related non-daemon
- * threads might still be running due to open queueing connections.
- */
- cleanupWhenSafe();
}
private boolean isEDTRunning() {
- Map threads = java.security.AccessController
- .doPrivileged(new java.security.PrivilegedAction