use of io.quarkus.dev.spi.DevModeType in project quarkus by quarkusio.
the class ExtensionLoader method loadStepsFrom.
/**
* Load all the build steps from the given class loader.
*
* @param classLoader the class loader
* @param buildSystemProps the build system properties to use
* @param launchMode launch mode
* @param configCustomizer configuration customizer
* @return a consumer which adds the steps to the given chain builder
* @throws IOException if the class loader could not load a resource
* @throws ClassNotFoundException if a build step class is not found
*/
public static Consumer<BuildChainBuilder> loadStepsFrom(ClassLoader classLoader, Properties buildSystemProps, ApplicationModel appModel, LaunchMode launchMode, DevModeType devModeType) throws IOException, ClassNotFoundException {
// populate with all known types
List<Class<?>> roots = new ArrayList<>();
for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, CONFIG_ROOTS_LIST)) {
final ConfigRoot annotation = clazz.getAnnotation(ConfigRoot.class);
if (annotation == null) {
cfgLog.warnf("Ignoring configuration root %s because it has no annotation", clazz);
} else {
roots.add(clazz);
}
}
final BuildTimeConfigurationReader reader = new BuildTimeConfigurationReader(roots);
// now prepare & load the build configuration
final SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false, launchMode);
final DefaultValuesConfigurationSource ds1 = new DefaultValuesConfigurationSource(reader.getBuildTimePatternMap());
final DefaultValuesConfigurationSource ds2 = new DefaultValuesConfigurationSource(reader.getBuildTimeRunTimePatternMap());
final PropertiesConfigSource pcs = new PropertiesConfigSource(buildSystemProps, "Build system");
final Map<String, String> platformProperties = appModel.getPlatformProperties();
if (platformProperties.isEmpty()) {
builder.withSources(ds1, ds2, pcs);
} else {
final KeyMap<String> props = new KeyMap<>(platformProperties.size());
for (Map.Entry<String, String> prop : platformProperties.entrySet()) {
props.findOrAdd(new NameIterator(prop.getKey())).putRootValue(prop.getValue());
}
final KeyMapBackedConfigSource platformConfigSource = new KeyMapBackedConfigSource("Quarkus platform", // (see io.quarkus.deployment.configuration.DefaultValuesConfigurationSource)
Integer.MIN_VALUE + 1000, props);
builder.withSources(ds1, ds2, platformConfigSource, pcs);
}
for (ConfigClassWithPrefix mapping : reader.getBuildTimeVisibleMappings()) {
builder.withMapping(mapping.getKlass(), mapping.getPrefix());
}
final SmallRyeConfig src = builder.build();
// install globally
QuarkusConfigFactory.setConfig(src);
final ConfigProviderResolver cpr = ConfigProviderResolver.instance();
try {
cpr.releaseConfig(cpr.getConfig());
} catch (IllegalStateException ignored) {
// just means no config was installed, which is fine
}
final BuildTimeConfigurationReader.ReadResult readResult = reader.readConfiguration(src);
final BooleanSupplierFactoryBuildItem bsf = new BooleanSupplierFactoryBuildItem(readResult, launchMode, devModeType);
Consumer<BuildChainBuilder> result = Functions.discardingConsumer();
// BooleanSupplier factory
result = result.andThen(bcb -> bcb.addBuildStep(bc -> {
bc.produce(bsf);
}).produces(BooleanSupplierFactoryBuildItem.class).build());
// the proxy objects used for run time config in the recorders
Map<Class<?>, Object> proxies = new HashMap<>();
for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, "META-INF/quarkus-build-steps.list")) {
try {
result = result.andThen(ExtensionLoader.loadStepsFromClass(clazz, readResult, proxies, bsf));
} catch (Throwable e) {
throw new RuntimeException("Failed to load steps from " + clazz, e);
}
}
// this has to be an identity hash map else the recorder will get angry
Map<Object, FieldDescriptor> rootFields = new IdentityHashMap<>();
Map<Object, ConfigClassWithPrefix> mappingClasses = new IdentityHashMap<>();
for (Map.Entry<Class<?>, Object> entry : proxies.entrySet()) {
// ConfigRoot
RootDefinition root = readResult.getAllRootsByClass().get(entry.getKey());
if (root != null) {
rootFields.put(entry.getValue(), root.getDescriptor());
continue;
}
// ConfigMapping
ConfigClassWithPrefix mapping = readResult.getAllMappings().get(entry.getKey());
if (mapping != null) {
mappingClasses.put(entry.getValue(), mapping);
continue;
}
throw new IllegalStateException("No config found for " + entry.getKey());
}
result = result.andThen(bcb -> bcb.addBuildStep(bc -> {
bc.produce(new ConfigurationBuildItem(readResult));
bc.produce(new RunTimeConfigurationProxyBuildItem(proxies));
ObjectLoader rootLoader = new ObjectLoader() {
public ResultHandle load(final BytecodeCreator body, final Object obj, final boolean staticInit) {
return body.readStaticField(rootFields.get(obj));
}
public boolean canHandleObject(final Object obj, final boolean staticInit) {
return rootFields.containsKey(obj);
}
};
ObjectLoader mappingLoader = new ObjectLoader() {
@Override
public ResultHandle load(final BytecodeCreator body, final Object obj, final boolean staticInit) {
ConfigClassWithPrefix mapping = mappingClasses.get(obj);
MethodDescriptor getConfig = MethodDescriptor.ofMethod(ConfigProvider.class, "getConfig", Config.class);
ResultHandle config = body.invokeStaticMethod(getConfig);
MethodDescriptor getMapping = MethodDescriptor.ofMethod(SmallRyeConfig.class, "getConfigMapping", Object.class, Class.class, String.class);
return body.invokeVirtualMethod(getMapping, config, body.loadClass(mapping.getKlass()), body.load(mapping.getPrefix()));
}
@Override
public boolean canHandleObject(final Object obj, final boolean staticInit) {
return mappingClasses.containsKey(obj);
}
};
bc.produce(new BytecodeRecorderObjectLoaderBuildItem(rootLoader));
bc.produce(new BytecodeRecorderObjectLoaderBuildItem(mappingLoader));
}).produces(ConfigurationBuildItem.class).produces(RunTimeConfigurationProxyBuildItem.class).produces(BytecodeRecorderObjectLoaderBuildItem.class).build());
return result;
}
use of io.quarkus.dev.spi.DevModeType in project quarkus by quarkusio.
the class IsolatedDevModeMain method accept.
// the main entry point, but loaded inside the augmentation class loader
@Override
public void accept(CuratedApplication o, Map<String, Object> params) {
// setup the dev mode thread pool for NIO
System.setProperty("java.nio.channels.DefaultThreadPool.threadFactory", "io.quarkus.dev.io.NioThreadPoolThreadFactory");
Timing.staticInitStarted(o.getBaseRuntimeClassLoader(), false);
// https://github.com/quarkusio/quarkus/issues/9748
// if you have an app with all daemon threads then the app thread
// may be the only thread keeping the JVM alive
// during the restart process when this thread is stopped then
// the JVM will die
// we start this thread to keep the JVM alive until the shutdown hook is run
// even for command mode we still want the JVM to live until it receives
// a signal to make the 'press enter to restart' function to work
new Thread(new Runnable() {
@Override
public void run() {
try {
shutdownLatch.await();
} catch (InterruptedException ignore) {
}
}
}, "Quarkus Devmode keep alive thread").start();
try {
curatedApplication = o;
Object potentialContext = params.get(DevModeContext.class.getName());
if (potentialContext instanceof DevModeContext) {
context = (DevModeContext) potentialContext;
} else {
// this was from the external class loader
// we need to copy it into this one
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(out);
oo.writeObject(potentialContext);
context = (DevModeContext) new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())).readObject();
}
augmentAction = new AugmentActionImpl(curatedApplication, Collections.singletonList(new Consumer<BuildChainBuilder>() {
@Override
public void accept(BuildChainBuilder buildChainBuilder) {
buildChainBuilder.addBuildStep(new BuildStep() {
@Override
public void execute(BuildContext context) {
// we need to make sure all hot reloadable classes are application classes
context.produce(new ApplicationClassPredicateBuildItem(new Predicate<String>() {
@Override
public boolean test(String s) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
// if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl.getElementsWithResource(s.replace('.', '/') + ".class", true);
return !res.isEmpty();
}
}));
}
}).produces(ApplicationClassPredicateBuildItem.class).build();
}
}), Collections.emptyList());
List<CodeGenData> codeGens = new ArrayList<>();
QuarkusClassLoader deploymentClassLoader = curatedApplication.createDeploymentClassLoader();
for (DevModeContext.ModuleInfo module : context.getAllModules()) {
if (!module.getSourceParents().isEmpty() && module.getPreBuildOutputDir() != null) {
// it's null for remote dev
codeGens.addAll(CodeGenerator.init(deploymentClassLoader, module.getSourceParents(), Paths.get(module.getPreBuildOutputDir()), Paths.get(module.getTargetDir()), sourcePath -> module.addSourcePathFirst(sourcePath.toAbsolutePath().toString())));
}
}
RuntimeUpdatesProcessor.INSTANCE = setupRuntimeCompilation(context, (Path) params.get(APP_ROOT), (DevModeType) params.get(DevModeType.class.getName()));
if (RuntimeUpdatesProcessor.INSTANCE != null) {
RuntimeUpdatesProcessor.INSTANCE.checkForFileChange();
RuntimeUpdatesProcessor.INSTANCE.checkForChangedClasses(true);
}
firstStart(deploymentClassLoader, codeGens);
// doStart(false, Collections.emptySet());
if (deploymentProblem != null || RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() != null) {
if (context.isAbortOnFailedStart()) {
throw new RuntimeException(deploymentProblem == null ? RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() : deploymentProblem);
}
}
shutdownThread = new Thread(new Runnable() {
@Override
public void run() {
shutdownLatch.countDown();
synchronized (DevModeMain.class) {
if (runner != null) {
try {
close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}, "Quarkus Shutdown Thread");
Runtime.getRuntime().addShutdownHook(shutdownThread);
} catch (Exception e) {
close();
throw new RuntimeException(e);
}
}
use of io.quarkus.dev.spi.DevModeType in project quarkus by quarkusio.
the class RuntimeUpdatesProcessor method doScan.
public boolean doScan(boolean userInitiated, boolean forceRestart) {
if (!liveReloadEnabled && !forceRestart) {
return false;
}
scanLock.lock();
try {
final long startNanoseconds = System.nanoTime();
for (Runnable step : preScanSteps) {
try {
step.run();
} catch (Throwable t) {
log.error("Pre Scan step failed", t);
}
}
ClassScanResult changedClassResults = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, false, main, false);
Set<String> filesChanged = checkForFileChange(DevModeContext.ModuleInfo::getMain, main);
boolean configFileRestartNeeded = forceRestart || filesChanged.stream().map(main.watchedFilePaths::get).anyMatch(Boolean.TRUE::equals);
boolean instrumentationChange = false;
List<Path> changedFilesForRestart = new ArrayList<>();
if (configFileRestartNeeded) {
changedFilesForRestart.addAll(filesChanged.stream().filter(fn -> Boolean.TRUE.equals(main.watchedFilePaths.get(fn))).map(Paths::get).collect(Collectors.toList()));
}
changedFilesForRestart.addAll(changedClassResults.getChangedClasses());
changedFilesForRestart.addAll(changedClassResults.getAddedClasses());
changedFilesForRestart.addAll(changedClassResults.getDeletedClasses());
if (ClassChangeAgent.getInstrumentation() != null && lastStartIndex != null && !configFileRestartNeeded && devModeType != DevModeType.REMOTE_LOCAL_SIDE && instrumentationEnabled()) {
// using the JDK instrumentation API (assuming we were started with the javaagent)
if (changedClassResults.deletedClasses.isEmpty() && changedClassResults.addedClasses.isEmpty() && !changedClassResults.changedClasses.isEmpty()) {
try {
Indexer indexer = new Indexer();
// attempt to use the instrumentation API
ClassDefinition[] defs = new ClassDefinition[changedClassResults.changedClasses.size()];
int index = 0;
for (Path i : changedClassResults.changedClasses) {
byte[] bytes = Files.readAllBytes(i);
String name = indexer.index(new ByteArrayInputStream(bytes)).name().toString();
defs[index++] = new ClassDefinition(Thread.currentThread().getContextClassLoader().loadClass(name), classTransformers.apply(name, bytes));
}
Index current = indexer.complete();
boolean ok = !disableInstrumentationForIndexPredicate.test(current);
if (ok) {
for (ClassInfo clazz : current.getKnownClasses()) {
ClassInfo old = lastStartIndex.getClassByName(clazz.name());
if (!ClassComparisonUtil.isSameStructure(clazz, old) || disableInstrumentationForClassPredicate.test(clazz)) {
ok = false;
break;
}
}
}
if (ok) {
log.info("Application restart not required, replacing classes via instrumentation");
ClassChangeAgent.getInstrumentation().redefineClasses(defs);
instrumentationChange = true;
}
} catch (Exception e) {
log.error("Failed to replace classes via instrumentation", e);
instrumentationChange = false;
}
}
}
if (compileProblem != null) {
return false;
}
// if there is a deployment problem we always restart on scan
// this is because we can't setup the config file watches
// in an ideal world we would just check every resource file for changes, however as everything is already
// all broken we just assume the reason that they have refreshed is because they have fixed something
// trying to watch all resource files is complex and this is likely a good enough solution for what is already an edge case
boolean restartNeeded = !instrumentationChange && (changedClassResults.isChanged() || (IsolatedDevModeMain.deploymentProblem != null && userInitiated) || configFileRestartNeeded);
if (restartNeeded) {
String changeString = changedFilesForRestart.stream().map(Path::getFileName).map(Object::toString).collect(Collectors.joining(", "));
if (!changeString.isEmpty()) {
log.infof("Restarting quarkus due to changes in %s.", changeString);
} else if (forceRestart && userInitiated) {
log.info("Restarting as requested by the user.");
}
restartCallback.accept(filesChanged, changedClassResults);
long timeNanoSeconds = System.nanoTime() - startNanoseconds;
log.infof("Live reload total time: %ss ", Timing.convertToBigDecimalSeconds(timeNanoSeconds));
if (TimeUnit.SECONDS.convert(timeNanoSeconds, TimeUnit.NANOSECONDS) >= 4 && !instrumentationEnabled()) {
if (!instrumentationLogPrinted) {
instrumentationLogPrinted = true;
log.info("Live reload took more than 4 seconds, you may want to enable instrumentation based reload (quarkus.live-reload.instrumentation=true). This allows small changes to take effect without restarting Quarkus.");
}
}
return true;
} else if (!filesChanged.isEmpty()) {
for (Consumer<Set<String>> consumer : noRestartChangesConsumers) {
try {
consumer.accept(filesChanged);
} catch (Throwable t) {
log.error("Changed files consumer failed", t);
}
}
log.infof("Files changed but restart not needed - notified extensions in: %ss ", Timing.convertToBigDecimalSeconds(System.nanoTime() - startNanoseconds));
} else if (instrumentationChange) {
log.infof("Live reload performed via instrumentation, no restart needed, total time: %ss ", Timing.convertToBigDecimalSeconds(System.nanoTime() - startNanoseconds));
}
return false;
} finally {
scanLock.unlock();
}
}
use of io.quarkus.dev.spi.DevModeType in project quarkus by quarkusio.
the class TestTracingProcessor method startTesting.
@BuildStep(onlyIf = IsDevelopment.class)
@Produce(LogHandlerBuildItem.class)
@Produce(TestSetupBuildItem.class)
@Produce(ServiceStartBuildItem.class)
void startTesting(TestConfig config, LiveReloadBuildItem liveReloadBuildItem, LaunchModeBuildItem launchModeBuildItem, List<TestListenerBuildItem> testListenerBuildItems) {
if (TestSupport.instance().isEmpty() || config.continuousTesting == TestConfig.Mode.DISABLED || config.flatClassPath) {
return;
}
DevModeType devModeType = launchModeBuildItem.getDevModeType().orElse(null);
if (devModeType == null || !devModeType.isContinuousTestingSupported()) {
return;
}
if (testingSetup) {
return;
}
testingSetup = true;
TestSupport testSupport = TestSupport.instance().get();
for (TestListenerBuildItem i : testListenerBuildItems) {
testSupport.addListener(i.listener);
}
testSupport.setConfig(config);
testSupport.setTags(config.includeTags.orElse(Collections.emptyList()), config.excludeTags.orElse(Collections.emptyList()));
testSupport.setPatterns(config.includePattern.orElse(null), config.excludePattern.orElse(null));
testSupport.setEngines(config.includeEngines.orElse(Collections.emptyList()), config.excludeEngines.orElse(Collections.emptyList()));
testSupport.setConfiguredDisplayTestOutput(config.displayTestOutput);
testSupport.setTestType(config.type);
if (!liveReloadBuildItem.isLiveReload()) {
if (config.continuousTesting == TestConfig.Mode.ENABLED) {
testSupport.start();
} else if (config.continuousTesting == TestConfig.Mode.PAUSED) {
testSupport.stop();
}
}
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
((QuarkusClassLoader) cl.parent()).addCloseTask(ContinuousTestingSharedStateManager::reset);
}
Aggregations