Skip to content
Open
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 @@ -88,8 +88,9 @@ public static Object getTranslatedValue(Object value) {
if (stringValue.indexOf('$') == -1) {
return value;
}
// We first search for alias in the value which is much faster than loading the store..
// This speeds up the startup and avoids loading the store if it's not needed
if (getAlias(stringValue) != null) {
// First search for alias in value, it's faster than loading alias store. If no alias in value, store doesn't need to load. Speeds up startup.
DomainScopedPasswordAliasStore dasPasswordAliasStore = domainPasswordAliasStore();
if (dasPasswordAliasStore != null) {
try {
Expand All @@ -101,7 +102,6 @@ public static Object getTranslatedValue(Object value) {
}
}
}

// Perform property substitution in the value
// The loop limit is imposed to prevent infinite looping to values
// such as a=${a} or a=foo ${b} and b=bar {$a}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.lang.System.Logger.Level;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -63,6 +64,7 @@ class AutoDisposableGlassFish extends GlassFishImpl {
// If there are custom configurations like http.port, https.port, jmx.port then configure them.
CommandRunner commandRunner = null;
Set<String> knownPropertyPrefixes = new HashSet<>();
ArrayList<String> configPropertiesToSet = new ArrayList<>();
for (String key : gfProps.getPropertyNames()) {
String propertyName = key;
if (key.startsWith(GENERAL_CONFIG_PROP_PREFIX)) {
Expand Down Expand Up @@ -92,10 +94,12 @@ class AutoDisposableGlassFish extends GlassFishImpl {
continue;
}
}
CommandResult result = commandRunner.run("set", propertyName + "=" + propertyValue);
if (result.getExitStatus() != CommandResult.ExitStatus.SUCCESS) {
throw new GlassFishException(result.getOutput(), result.getFailureCause());
}
configPropertiesToSet.add(propertyName + "=" + propertyValue);
}

CommandResult result = commandRunner.run("set", configPropertiesToSet.toArray(String[]::new));
if (result.getExitStatus() != CommandResult.ExitStatus.SUCCESS) {
throw new GlassFishException(result.getOutput(), result.getFailureCause());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.sun.enterprise.module.bootstrap.ModuleStartup;
import com.sun.enterprise.module.bootstrap.StartupContext;
import com.sun.enterprise.module.common_impl.AbstractFactory;
import com.sun.enterprise.util.Utility;

import java.io.File;
import java.io.FileOutputStream;
Expand All @@ -35,15 +36,20 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer;
import java.util.logging.Logger;

import org.glassfish.embeddable.CommandRunner;
import org.glassfish.embeddable.GlassFish;
import org.glassfish.embeddable.GlassFishException;
import org.glassfish.embeddable.GlassFishProperties;
import org.glassfish.embeddable.GlassFishRuntime;
import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.hk2.utilities.DuplicatePostProcessor;
import org.glassfish.main.boot.log.LogFacade;

Expand Down Expand Up @@ -89,18 +95,25 @@ public synchronized GlassFish newGlassFish(GlassFishProperties glassFishProperti
properties.putAll(glassFishProperties.getProperties());

final GlassFishProperties gfProps = new GlassFishProperties(properties);
setEnv(gfProps);
final CompletableFuture<Void> setEnvFuture = setEnv(gfProps);

final StartupContext startupContext = new StartupContext(gfProps.getProperties());
final ModulesRegistry modulesRegistry = AbstractFactory.getInstance().createModulesRegistry();
// Building the serviceLocator takes about 600ms, around 1/5
final ServiceLocator serviceLocator = main.createServiceLocator(modulesRegistry, startupContext,
List.of(new EmbeddedInhabitantsParser(), new DuplicatePostProcessor()), null);

preloadEssentialServicesInBackground(serviceLocator);

final ModuleStartup gfKernel = main.findStartupService(modulesRegistry, serviceLocator, null,
startupContext);

final Consumer<GlassFish> onDispose = gf -> glassFishInstances.remove(gfProps.getInstanceRoot());
final GlassFish glassFish = new AutoDisposableGlassFish(gfKernel, serviceLocator, gfProps, onDispose);

// We need to complete the setup before creating AutoDisposableGlassFish
setEnvFuture.join();

final GlassFish glassFish = new AutoDisposableGlassFish(gfKernel, serviceLocator, gfProps, onDispose); // takes 900ms
glassFishInstances.put(gfProps.getInstanceRoot(), glassFish);

return glassFish;
Expand All @@ -111,6 +124,35 @@ public synchronized GlassFish newGlassFish(GlassFishProperties glassFishProperti
}
}

// Preloads some services in background. They will always be needed during startup.
// Instead of loading them lazily in the main thread, we load them immediately in background so that they are loaded sooner
private void preloadEssentialServicesInBackground(final ServiceLocator serviceLocator) {
// Preload command runner. AutoDisposableGlassFish always needs to run it
// - first load preloads all the singleton dependencies which take about 500ms to initialize.
// It's typically not loaded in time before it's needed on the main thread but it still decreases
// the time the main thread needs to wait until the initialization completes by about 2/3, to about 150ms
runAsynchWithThreadContext(() -> serviceLocator.getService(CommandRunner.class), Thread.currentThread());

// Preload the service for the set command (name="set") in a background thread.
// AutoDisposableGlassFish always needs to run it so it can retrieve it faster
// - we load the singleton GetSetModularityHelper dependency heere eagerly because it takes about 250ms to initialize
// Again, it doesn't load completely in time before it's needed on the main thread but decreases the wait time on the main to about 80ms
runAsynchWithThreadContext(
() -> {
final ActiveDescriptor<?> setCommandDescriptor = serviceLocator.getBestDescriptor(BuilderHelper.createNameFilter("set"));
serviceLocator.getServiceHandle(setCommandDescriptor)
.getService();
},
Thread.currentThread());
}

private static void runAsynchWithThreadContext(Utility.RunnableWithException action, Thread spawningThread) {
final ClassLoader contextClassLoader = spawningThread.getContextClassLoader();
ForkJoinPool.commonPool().submit(() -> {
Utility.runWithContextClassLoader(contextClassLoader, action);
});
}

@Override
public synchronized void shutdown() throws GlassFishException {
for (GlassFish glassFish : glassFishInstances.values()) {
Expand All @@ -130,7 +172,15 @@ public synchronized void shutdown() throws GlassFishException {
}
}

private void setEnv(GlassFishProperties gfProps) throws Exception {
/*
Unpacks the domain directory and sets up the properties related to the environment

Asynchronously completes tasks that aren't essential during startup to move them from the main thread.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance I fail to see where is this async hidden here. Isn't fully completed Future returned from here? And everything happens in the same Thread that calls setEnv? I think thenRun, unlike thenRunAsync also runs in the same Thread that given Future is being computed in.

🤔

The returned future should be joined before creating AutoDisposableGlassFish, which applies properties
that might depend on having the environment completely set up.
*/
private CompletableFuture<Void> setEnv(GlassFishProperties gfProps) throws Exception {
CompletableFuture<Void> asyncResult = CompletableFuture.completedFuture(null);
String instanceRootValue = gfProps.getInstanceRoot();
if (instanceRootValue == null) {
instanceRootValue = createTempInstanceRoot(gfProps);
Expand All @@ -145,7 +195,13 @@ private void setEnv(GlassFishProperties gfProps) throws Exception {
if (installRootValue == null) {
installRootValue = instanceRoot.getAbsolutePath();
gfProps.setProperty("-type", "EMBEDDED");
JarUtil.extractRars(installRootValue);
final String installRootFinalValue = installRootValue;
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
asyncResult = asyncResult.thenRun(
() -> {
Utility.runWithContextClassLoader(contextClassLoader,
() -> JarUtil.extractRars(installRootFinalValue));
});
}
JarUtil.setEnv(installRootValue);

Expand All @@ -159,6 +215,8 @@ private void setEnv(GlassFishProperties gfProps) throws Exception {
// StartupContext requires the installRoot to be set in the GlassFishProperties.
gfProps.setProperty(INSTALL_ROOT.getPropertyName(), installRoot.getAbsolutePath());
gfProps.setProperty(BootstrapKeys.INSTALL_ROOT_URI_PROP_NAME, installRoot.toURI().toString());

return asyncResult;
}

private String createTempInstanceRoot(GlassFishProperties gfProps) throws Exception {
Expand Down
Loading