Skip to content

Commit

Permalink
Solve ClassPathResourceLoader issue with jar
Browse files Browse the repository at this point in the history
Copying a few lines of code from `ClassPathUtils.processAsPath`
  • Loading branch information
ia3andy committed Jul 7, 2020
1 parent 21ac86e commit 84ac8d8
Show file tree
Hide file tree
Showing 61 changed files with 554 additions and 225 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: gradle
type: buildtool
spec:
base:
local-data:
data:
quarkus.plugin.id: io.quarkus
version.kotlin: 1.3.72
version.scala: 2.12.8
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: maven
type: buildtool
default: true
type-fallback: true
spec:
base:
local-data:
data:
version.kotlin: 1.3.72
version.scala: 2.12.8
version.scala-maven-plugin: 4.1.1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
name: config-properties
type: config
default: true
type-fallback: true
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
name: config-yaml
type: config
type: config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
name: java
type: language
default: true
type-fallback: true
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: quarkus
type: project
default: true
type-fallback: true
spec:
base:
shared-data:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ ref: commandmode
example: true
spec:
base:
local-data:
data:
greeting.message: "hello"
greeting.default-name: "commando"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
name: docker
preselected: true
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ref: resteasy
example: true
spec:
base:
local-data:
data:
rest.path: "/hello"
rest.response: "hello"
test-dependencies:
Expand Down
109 changes: 109 additions & 0 deletions independent-projects/tools/devtools-common/codestarts.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
= Quarkus - Codestarts

This guide explains how to create and configure a Quarkus Codestart for an extension.

== Description

"Codestarts" is the name we gave to our Quarkus quickstart code generation system. Codestarts are meant to provide a personalized Quarkus getting started experience and really show the Quarkus breadth.
Quarkus extension are able to provide one or more well defined Codestarts which will contain all the necessary resources to bring user who pick that extension, everything he needs to get started with it.

== How it works

There two kind of Codestarts contributing to generating a project,
the kind where we require to have only one for a project (called single) and the others (called other).

Singles:

* project: The base (ex: a Quarkus project)
* buildtool: The build tool (ex: Maven, Gradle, Gradle with Kotlin DSL)
* language: The coding language (ex: Java, Kotlin, Scala)
* config: The config type (ex: yaml, properties)
* [custom]: Any custom type that must be single

Others: Whats comes of top as addition (ex: dockerfiles, example code for extensions, ...)

Each Codestart consists of:

. a specific directory
. a codestart.yml file
. optionally some templates which follow a structure and naming convention

*NOTE* The codestart.yml file and the directory structure follow the same principle, it can optionally contain a base and/or some language overrides.

=== Example Codestart:

Directory structure
* my-codestart/codestart.yml
* my-codestart/base/
* my-codestart/java/
* my-codestart/kotlin/

codestart.yml:
[source,yaml]
----
name: my-codestart
spec:
base:
...
java:
...
----

=== Project generation

When generating a Quarkus project:

. codestarts to use are resolved depending on the input
. codestarts shared data is processed to make it available for all codestarts
. The relevant files are processed:
** Only the files that are related to the selected language are processed
** The files are processed differently based on a naming convention.
** The data is used to render Qute templates
** We always process in this order: language, project, buildtool, config, custom, other.

The data used to generate a specific Codestart is a merge of:
. The data of the Codestart to generate
. All Codestarts "shared" data
. The user input
. Some specific post processing (ex: adding dependencies)

*NOTE* The data (shared or not) can also be specific to a language.

=== Directory structure

*NOTE* `codestart.yml` is the only required file.

* codestart.yml must be at the root of the Codestart
* `./base` contains all the files that will be processed
* `./[languageName]` contains all the files that will be processed if the specified language has been selected

=== codestart.yml

codestart.yml:
[source,yaml]
----
name: the unique name - REQUIRED
ref: the reference name (not unique) to use for extension matching (else the name is used)
type: the type [other (default), project, buildtool, language, config] or any custom type
type-fallback: flag to indicate that it should be selected as fallback when no codestart has been selected with type - SINGLE
preselected: flag to indicate that it should be pre-selected - OTHER.
example: flag to indicate that it is optional example code that can be disabled by the user - OTHER.
spec:
[language-name]: the language specifications (base, java, ...)
data: a flat map of data to use only for this codestart
shared-data: a flat map of data to use accross all codestarts
dependencies: a list of dependencies to add when this is selected
test-dependencies: a list of test dependencies to add when this is selected
----

=== Files naming convention

* containing `.qute` will be processed with Qute
* containing `.include-qute` is used as a Qute template for inclusion.

Example: When using `{#include [name]}` in a template, it will look for `[name].include-qute` in the language dir, then in the base dir.

* containing `.append` will be appended together
* other files are just copied

*NOTE* there is also something specific for `pom.xml` but this should not be available out of internal codestarts.
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,24 @@ final class CodestartLoader {
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);

private static final String CODESTARTS_DIR_BASE = "codestarts/base";
private static final String CODESTARTS_DIR_EXTENSIONS = "codestarts/extensions";
private static final String CODESTARTS_DIR_CORE = "codestarts/core";
private static final String CODESTARTS_DIR_OTHER = "codestarts/other";

private CodestartLoader() {
}

public static List<Codestart> loadAddCodestarts(CodestartInput input) throws IOException {
return Stream.concat(CodestartLoader.loadBaseCodestarts(input.getDescriptor()).stream(),
CodestartLoader.loadExtensionsCodestarts(input.getDescriptor()).stream()).collect(Collectors.toList());
public static List<Codestart> loadAllCodestarts(CodestartInput input) throws IOException {
return Stream.concat(CodestartLoader.loadCoreCodestarts(input.getDescriptor()).stream(),
CodestartLoader.loadOtherCodestarts(input.getDescriptor()).stream()).collect(Collectors.toList());
}

public static Collection<Codestart> loadBaseCodestarts(final QuarkusPlatformDescriptor descriptor) throws IOException {
return loadCodestarts(descriptor, CODESTARTS_DIR_BASE);
public static Collection<Codestart> loadCoreCodestarts(final QuarkusPlatformDescriptor descriptor) throws IOException {
return loadCodestarts(descriptor, CODESTARTS_DIR_CORE);
}

public static Collection<Codestart> loadExtensionsCodestarts(final QuarkusPlatformDescriptor descriptor)
public static Collection<Codestart> loadOtherCodestarts(final QuarkusPlatformDescriptor descriptor)
throws IOException {
return loadCodestarts(descriptor, CODESTARTS_DIR_EXTENSIONS);
return loadCodestarts(descriptor, CODESTARTS_DIR_OTHER);
}

static Collection<Codestart> loadCodestarts(final QuarkusPlatformDescriptor descriptor, final String directoryName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,32 @@

import static io.quarkus.devtools.project.codegen.codestarts.Codestart.BASE_LANGUAGE;

import io.fabric8.maven.Maven;
import io.fabric8.maven.merge.SmartModelMerger;
import io.quarkus.devtools.project.codegen.codestarts.reader.CodestartFileReader;
import io.quarkus.devtools.project.codegen.codestarts.reader.QuteCodestartFileReader;
import io.quarkus.devtools.project.codegen.codestarts.writer.AppenderCodestartFileWriter;
import io.quarkus.devtools.project.codegen.codestarts.writer.CodestartFileWriter;
import io.quarkus.devtools.project.codegen.codestarts.writer.MavenPomMergeCodestartFileWriter;
import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor;
import java.io.IOException;
import java.io.StringReader;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import org.apache.maven.model.Model;

final class CodestartProcessor {

private static final List<CodestartFileReader> readers = Collections.unmodifiableList(Arrays.asList(
new QuteCodestartFileReader()));
private static final List<CodestartFileWriter> writers = Collections.unmodifiableList(Arrays.asList(
new AppenderCodestartFileWriter(),
new MavenPomMergeCodestartFileWriter()));

private CodestartProcessor() {
}

Expand Down Expand Up @@ -52,35 +63,35 @@ static void processCodestartDir(final String languageName,
try {
Files.walk(sourceDirectory)
.filter(path -> !path.equals(sourceDirectory))
.forEach(path -> {
.forEach(sourcePath -> {
try {
final Path relativePath = sourceDirectory.relativize(path);
if (Files.isDirectory(path)) {
final Path relativePath = sourceDirectory.relativize(sourcePath);
if (Files.isDirectory(sourcePath)) {
return;
} else {
final String fileName = relativePath.getFileName().toString();
final Path targetPath = targetProjectDirectory.resolve(relativePath.toString());
if (fileName.contains(".part")) {
// TODO we need some kind of PartCombiner interface with a "matcher"
if (fileName.equals("pom.part.qute.xml")) {
processMavenPart(path, languageName, data, targetPath.getParent().resolve("pom.xml"));
} else if (fileName.equals("README.part.qute.md")) {
processReadmePart(path, languageName, data,
targetPath.getParent().resolve("README.md"));
} else {
throw new IllegalStateException("Unsupported part file: " + path);
}
} else if (fileName.contains(".qute")) {
if (fileName.endsWith(".include.qute")) {
//ignore includes
return;
}
// Template file
processQuteFile(path, languageName, data,
targetPath.getParent().resolve(fileName.replace(".qute", "")));
final Optional<CodestartFileReader> possibleReader = readers.stream()
.filter(r -> r.matches(fileName))
.findFirst();
final Optional<CodestartFileWriter> possibleWriter = writers.stream()
.filter(r -> r.matches(fileName))
.findFirst();

if (!possibleWriter.isPresent() && !possibleReader.isPresent()) {
processStaticFile(sourcePath, targetPath);
} else {
// Static file
processStaticFile(path, targetPath);
final CodestartFileReader reader = possibleReader.orElse(CodestartFileReader.DEFAULT);
final Optional<String> content = reader.read(sourcePath,
languageName, data);
if (content.isPresent()) {
final CodestartFileWriter writer = possibleWriter.orElse(CodestartFileWriter.DEFAULT);
final String cleanFileName = writer.cleanFileName(reader.cleanFileName(fileName));
final Path parent = targetPath.getParent();
Files.createDirectories(parent);
writer.process(content.get(), parent.resolve(cleanFileName),
languageName, data);
}
}
}
} catch (final IOException e) {
Expand All @@ -93,40 +104,9 @@ static void processCodestartDir(final String languageName,

}

private static void processReadmePart(Path path, String languageName, Map<String, Object> data, Path targetPath)
throws IOException {
if (!Files.exists(targetPath)) {
throw new IllegalStateException(
"Using .part is not possible when the target file does not exist already: " + path + " -> " + targetPath);
}
final String renderedContent = "\n" + CodestartQute.processQuteContent(path, languageName, data);
Files.write(targetPath, renderedContent.getBytes(), StandardOpenOption.APPEND);
}

private static void processMavenPart(Path path, String languageName, Map<String, Object> data, Path targetPath)
throws IOException {
if (!Files.exists(targetPath)) {
throw new IllegalStateException(
"Using .part is not possible when the target file does not exist already: " + path + " -> " + targetPath);
}
final Model targetModel = Maven.readModel(targetPath);
final SmartModelMerger merger = new SmartModelMerger();
final String content = CodestartQute.processQuteContent(path, languageName, data);
final Model sourceModel = Maven.readModel(new StringReader(content));
merger.merge(targetModel, sourceModel, true, null);
Maven.writeModel(targetModel);
}

private static void processStaticFile(Path path, Path targetPath) throws IOException {
Files.createDirectories(targetPath.getParent());
Files.copy(path, targetPath);
}

private static void processQuteFile(Path path, String languageName, Map<String, Object> data, Path targetPath)
throws IOException {
final String renderedContent = CodestartQute.processQuteContent(path, languageName, data);
Files.createDirectories(targetPath.getParent());
Files.write(targetPath, renderedContent.getBytes(), StandardOpenOption.CREATE_NEW);
Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING);
}

static void checkTargetDir(Path targetDirectory) throws IOException {
Expand Down
Loading

0 comments on commit 84ac8d8

Please sign in to comment.