# Directory Structure
```
├── .dockerignore
├── .gitignore
├── .mvn
│ └── wrapper
│ ├── .gitignore
│ ├── maven-wrapper.properties
│ └── MavenWrapperDownloader.java
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
└── src
└── main
├── docker
│ ├── Dockerfile.jvm
│ ├── Dockerfile.legacy-jar
│ ├── Dockerfile.native
│ └── Dockerfile.native-micro
├── java
│ └── org
│ └── acme
│ └── Backstage.java
└── resources
└── application.properties
```
# Files
--------------------------------------------------------------------------------
/.mvn/wrapper/.gitignore:
--------------------------------------------------------------------------------
```
1 | maven-wrapper.jar
2 |
```
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
```
1 | *
2 | !target/*-runner
3 | !target/*-runner.jar
4 | !target/lib/*
5 | !target/quarkus-app/*
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | #Maven
2 | target/
3 | pom.xml.tag
4 | pom.xml.releaseBackup
5 | pom.xml.versionsBackup
6 | release.properties
7 | .flattened-pom.xml
8 |
9 | # Eclipse
10 | .project
11 | .classpath
12 | .settings/
13 | bin/
14 |
15 | # IntelliJ
16 | .idea
17 | *.ipr
18 | *.iml
19 | *.iws
20 |
21 | # NetBeans
22 | nb-configuration.xml
23 |
24 | # Visual Studio Code
25 | .vscode
26 | .factorypath
27 |
28 | # OSX
29 | .DS_Store
30 |
31 | # Vim
32 | *.swp
33 | *.swo
34 |
35 | # patch
36 | *.orig
37 | *.rej
38 |
39 | # Local environment
40 | .env
41 |
42 | # Plugin directory
43 | /.quarkus/cli/plugins/
44 | # TLS Certificates
45 | .certs/
46 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Backstage MCP
2 |
3 | This is an example of using [Backstage](https://backstage.io) with [MCP](https://github.com/Backstage/mcp) via [Quarkus Backstage](https://github.com/quarkiverse/quarkus-backstage).
4 | The server has been tested with [Goose](https://github.com/block/goose). See below for details.
5 |
6 | ## Features
7 |
8 | - List available Backstage templates
9 | - Instantiate a template from the command line
10 |
11 | ## Requirements
12 | A [Backstage](https://backstage.io) installation is required.
13 | The installation needs to have enabled [Service to Service](https://backstage.io/docs/auth/service-to-service-auth/) communication.
14 | Users need to have access of the token used for such communication. The `yaml` snippet below shows where and how it's setup.
15 |
16 | ```yaml
17 | app:
18 | # ...
19 | backend:
20 | # ...
21 | auth:
22 | # ...
23 | externalAccess:
24 | - type: static
25 | options:
26 | token: <put your token here>
27 | subject: curl-requests
28 | ```
29 |
30 | ## Anatomy
31 |
32 | ### Dependencies
33 |
34 | The project is using:
35 |
36 | ```xml
37 | <dependency>
38 | <groupId>io.quarkiverse.mcp</groupId>
39 | <artifactId>quarkus-mcp-server-stdio</artifactId>
40 | <version>1.0.0.Alpha5</version>
41 | </dependency>
42 | ```
43 |
44 | for implementing an mcp server that reads from stdin and writes to stdout.
45 |
46 | It also uses:
47 |
48 | ```xml
49 | <dependency>
50 | <groupId>io.quarkiverse.backstage</groupId>
51 | <artifactId>quarkus-backstage</artifactId>
52 | <version>0.4.1</version>
53 | </dependency>
54 |
55 | ```
56 |
57 | For talking to the [Backstage](https://backstage.io) API.
58 |
59 | ### The implementation
60 |
61 | The implementation is pretty straight forward, as there is a tiny file needed:
62 | [[src/main/java/org/acme/Backstage.java]]
63 |
64 | ## Setting up goose
65 |
66 | [Goose](https://github.com/block/goose) is an local AI agent that runs as an interactive shell and is supports plugins (including mcp servers).
67 |
68 | To setup goose so that it uses this mcp server add the followng extension to your config.yaml:
69 |
70 | ```yaml
71 | quarkus-backstage-mcp:
72 | args:
73 | - --quiet
74 | - /home/iocanel/demo/backstage-mcp/target/quarkus-app/quarkus-run.jar
75 | cmd: jbang
76 | enabled: true
77 | envs: {}
78 | name: quarkus-backstage-mcp
79 | type: stdio
80 | ```
81 |
82 | ### Sample prompts
83 |
84 | #### Listing the templates
85 | ```bash
86 | list all the available backstage templates
87 | ```
88 |
89 | ### Instantiating a template
90 |
91 | To instantiate a template one needs values.yaml file containing the template parameters to use.
92 | The default values can be extracted from the template using the backstage CLI:
93 |
94 | ```sh
95 | quarkus backstage template info --show-default-values <template name>
96 | ```
97 |
98 | The output can be saved to a file, say `values.yaml` and then used to instantiate the template from a goose session:
99 |
100 | ```sh
101 | create a new project from template <template name> using values from values.yaml
102 | ```
103 |
104 |
```
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
```
1 |
```
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
```
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=source
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
20 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar
```
--------------------------------------------------------------------------------
/src/main/java/org/acme/Backstage.java:
--------------------------------------------------------------------------------
```java
1 | package org.acme;
2 |
3 | import java.util.List;
4 |
5 |
6 | import io.quarkiverse.backstage.client.BackstageClient;
7 | import io.quarkiverse.backstage.common.utils.Serialization;
8 | import io.quarkiverse.mcp.server.Tool;
9 | import io.quarkiverse.mcp.server.ToolArg;
10 | import jakarta.enterprise.context.ApplicationScoped;
11 | import jakarta.inject.Inject;
12 | import java.util.Map;
13 | import java.nio.file.Path;
14 | import com.fasterxml.jackson.core.type.TypeReference;
15 |
16 |
17 | @ApplicationScoped
18 | public class Backstage {
19 |
20 | @Inject
21 | BackstageClient client;
22 |
23 | @Tool(description = "List backstage templates")
24 | public List<String> listTemplates() {
25 | return client.entities().list("kind=template").stream().map(e -> e.getMetadata().getName()).toList();
26 | }
27 |
28 | @Tool(description = "Create a backstage project using a template")
29 | public String createProject(@ToolArg(description = "Template name") String templateName,
30 | @ToolArg(description = "Path to parameters file") String valuesFile) {
31 |
32 | Map<String, Object> values = Serialization.unmarshal(Path.of(valuesFile).toFile(), new TypeReference<Map<String, Object>>() {});
33 | return client.templates().withName(templateName).instantiate(values);
34 | }
35 | }
36 |
37 |
```
--------------------------------------------------------------------------------
/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
```java
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.net.Authenticator;
23 | import java.net.PasswordAuthentication;
24 | import java.net.URI;
25 | import java.net.URL;
26 | import java.nio.file.Files;
27 | import java.nio.file.Path;
28 | import java.nio.file.Paths;
29 | import java.nio.file.StandardCopyOption;
30 | import java.util.concurrent.ThreadLocalRandom;
31 |
32 | public final class MavenWrapperDownloader {
33 | private static final String WRAPPER_VERSION = "3.3.2";
34 |
35 | private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE"));
36 |
37 | public static void main(String[] args) {
38 | log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION);
39 |
40 | if (args.length != 2) {
41 | System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing");
42 | System.exit(1);
43 | }
44 |
45 | try {
46 | log(" - Downloader started");
47 | final URL wrapperUrl = URI.create(args[0]).toURL();
48 | final String jarPath = args[1].replace("..", ""); // Sanitize path
49 | final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize();
50 | downloadFileFromURL(wrapperUrl, wrapperJarPath);
51 | log("Done");
52 | } catch (IOException e) {
53 | System.err.println("- Error downloading: " + e.getMessage());
54 | if (VERBOSE) {
55 | e.printStackTrace();
56 | }
57 | System.exit(1);
58 | }
59 | }
60 |
61 | private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath)
62 | throws IOException {
63 | log(" - Downloading to: " + wrapperJarPath);
64 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
65 | final String username = System.getenv("MVNW_USERNAME");
66 | final char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
67 | Authenticator.setDefault(new Authenticator() {
68 | @Override
69 | protected PasswordAuthentication getPasswordAuthentication() {
70 | return new PasswordAuthentication(username, password);
71 | }
72 | });
73 | }
74 | Path temp = wrapperJarPath
75 | .getParent()
76 | .resolve(wrapperJarPath.getFileName() + "."
77 | + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp");
78 | try (InputStream inStream = wrapperUrl.openStream()) {
79 | Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING);
80 | Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING);
81 | } finally {
82 | Files.deleteIfExists(temp);
83 | }
84 | log(" - Downloader complete");
85 | }
86 |
87 | private static void log(String msg) {
88 | if (VERBOSE) {
89 | System.out.println(msg);
90 | }
91 | }
92 |
93 | }
94 |
```
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
```
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
3 | <modelVersion>4.0.0</modelVersion>
4 | <groupId>org.acme</groupId>
5 | <artifactId>backstage-mcp</artifactId>
6 | <version>1.0.0-SNAPSHOT</version>
7 |
8 | <properties>
9 | <compiler-plugin.version>3.14.0</compiler-plugin.version>
10 | <maven.compiler.release>21</maven.compiler.release>
11 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
12 | <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
13 | <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
14 | <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
15 | <quarkus.platform.version>3.19.4</quarkus.platform.version>
16 | <skipITs>true</skipITs>
17 | <surefire-plugin.version>3.5.2</surefire-plugin.version>
18 | </properties>
19 |
20 | <dependencyManagement>
21 | <dependencies>
22 | <dependency>
23 | <groupId>${quarkus.platform.group-id}</groupId>
24 | <artifactId>${quarkus.platform.artifact-id}</artifactId>
25 | <version>${quarkus.platform.version}</version>
26 | <type>pom</type>
27 | <scope>import</scope>
28 | </dependency>
29 | </dependencies>
30 | </dependencyManagement>
31 |
32 | <dependencies>
33 | <dependency>
34 | <groupId>io.quarkiverse.mcp</groupId>
35 | <artifactId>quarkus-mcp-server-stdio</artifactId>
36 | <version>1.0.0.Alpha5</version>
37 | </dependency>
38 | <dependency>
39 | <groupId>io.quarkiverse.backstage</groupId>
40 | <artifactId>quarkus-backstage</artifactId>
41 | <version>0.4.1</version>
42 | </dependency>
43 | <dependency>
44 | <groupId>io.quarkus</groupId>
45 | <artifactId>quarkus-arc</artifactId>
46 | </dependency>
47 | <dependency>
48 | <groupId>io.quarkus</groupId>
49 | <artifactId>quarkus-junit5</artifactId>
50 | <scope>test</scope>
51 | </dependency>
52 | </dependencies>
53 |
54 | <build>
55 | <plugins>
56 | <plugin>
57 | <groupId>${quarkus.platform.group-id}</groupId>
58 | <artifactId>quarkus-maven-plugin</artifactId>
59 | <version>${quarkus.platform.version}</version>
60 | <extensions>true</extensions>
61 | <executions>
62 | <execution>
63 | <goals>
64 | <goal>build</goal>
65 | <goal>generate-code</goal>
66 | <goal>generate-code-tests</goal>
67 | <goal>native-image-agent</goal>
68 | </goals>
69 | </execution>
70 | </executions>
71 | </plugin>
72 | <plugin>
73 | <artifactId>maven-compiler-plugin</artifactId>
74 | <version>${compiler-plugin.version}</version>
75 | <configuration>
76 | <parameters>true</parameters>
77 | </configuration>
78 | </plugin>
79 | <plugin>
80 | <artifactId>maven-surefire-plugin</artifactId>
81 | <version>${surefire-plugin.version}</version>
82 | <configuration>
83 | <systemPropertyVariables>
84 | <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
85 | <maven.home>${maven.home}</maven.home>
86 | </systemPropertyVariables>
87 | </configuration>
88 | </plugin>
89 | <plugin>
90 | <artifactId>maven-failsafe-plugin</artifactId>
91 | <version>${surefire-plugin.version}</version>
92 | <executions>
93 | <execution>
94 | <goals>
95 | <goal>integration-test</goal>
96 | <goal>verify</goal>
97 | </goals>
98 | </execution>
99 | </executions>
100 | <configuration>
101 | <systemPropertyVariables>
102 | <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
103 | <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
104 | <maven.home>${maven.home}</maven.home>
105 | </systemPropertyVariables>
106 | </configuration>
107 | </plugin>
108 | </plugins>
109 | </build>
110 |
111 | <profiles>
112 | <profile>
113 | <id>native</id>
114 | <activation>
115 | <property>
116 | <name>native</name>
117 | </property>
118 | </activation>
119 | <properties>
120 | <skipITs>false</skipITs>
121 | <quarkus.native.enabled>true</quarkus.native.enabled>
122 | </properties>
123 | </profile>
124 | </profiles>
125 | </project>
126 |
```
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
```
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Apache Maven Wrapper startup batch script, version 3.3.2
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
30 | @REM e.g. to debug Maven itself, use
31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
33 | @REM ----------------------------------------------------------------------------
34 |
35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
36 | @echo off
37 | @REM set title of command window
38 | title %0
39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
41 |
42 | @REM set %HOME% to equivalent of $HOME
43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
44 |
45 | @REM Execute a user defined script before this one
46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
50 | :skipRcPre
51 |
52 | @setlocal
53 |
54 | set ERROR_CODE=0
55 |
56 | @REM To isolate internal variables from possible post scripts, we use another setlocal
57 | @setlocal
58 |
59 | @REM ==== START VALIDATION ====
60 | if not "%JAVA_HOME%" == "" goto OkJHome
61 |
62 | echo. >&2
63 | echo Error: JAVA_HOME not found in your environment. >&2
64 | echo Please set the JAVA_HOME variable in your environment to match the >&2
65 | echo location of your Java installation. >&2
66 | echo. >&2
67 | goto error
68 |
69 | :OkJHome
70 | if exist "%JAVA_HOME%\bin\java.exe" goto init
71 |
72 | echo. >&2
73 | echo Error: JAVA_HOME is set to an invalid directory. >&2
74 | echo JAVA_HOME = "%JAVA_HOME%" >&2
75 | echo Please set the JAVA_HOME variable in your environment to match the >&2
76 | echo location of your Java installation. >&2
77 | echo. >&2
78 | goto error
79 |
80 | @REM ==== END VALIDATION ====
81 |
82 | :init
83 |
84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
85 | @REM Fallback to current working directory if not found.
86 |
87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
89 |
90 | set EXEC_DIR=%CD%
91 | set WDIR=%EXEC_DIR%
92 | :findBaseDir
93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
94 | cd ..
95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
96 | set WDIR=%CD%
97 | goto findBaseDir
98 |
99 | :baseDirFound
100 | set MAVEN_PROJECTBASEDIR=%WDIR%
101 | cd "%EXEC_DIR%"
102 | goto endDetectBaseDir
103 |
104 | :baseDirNotFound
105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
106 | cd "%EXEC_DIR%"
107 |
108 | :endDetectBaseDir
109 |
110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
111 |
112 | @setlocal EnableExtensions EnableDelayedExpansion
113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
115 |
116 | :endReadAdditionalConfig
117 |
118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
121 |
122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
123 |
124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
126 | )
127 |
128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
130 | if exist %WRAPPER_JAR% (
131 | if "%MVNW_VERBOSE%" == "true" (
132 | echo Found %WRAPPER_JAR%
133 | )
134 | ) else (
135 | if not "%MVNW_REPOURL%" == "" (
136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
137 | )
138 | if "%MVNW_VERBOSE%" == "true" (
139 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
140 | echo Downloading from: %WRAPPER_URL%
141 | )
142 |
143 | powershell -Command "&{"^
144 | "$webclient = new-object System.Net.WebClient;"^
145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
147 | "}"^
148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
149 | "}"
150 | if "%MVNW_VERBOSE%" == "true" (
151 | echo Finished downloading %WRAPPER_JAR%
152 | )
153 | )
154 | @REM End of extension
155 |
156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
157 | SET WRAPPER_SHA_256_SUM=""
158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
160 | )
161 | IF NOT %WRAPPER_SHA_256_SUM%=="" (
162 | powershell -Command "&{"^
163 | "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^
164 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
165 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
166 | " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
167 | " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
168 | " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
169 | " exit 1;"^
170 | "}"^
171 | "}"
172 | if ERRORLEVEL 1 goto error
173 | )
174 |
175 | @REM Provide a "standardized" way to retrieve the CLI args that will
176 | @REM work with both Windows and non-Windows executions.
177 | set MAVEN_CMD_LINE_ARGS=%*
178 |
179 | %MAVEN_JAVA_EXE% ^
180 | %JVM_CONFIG_MAVEN_PROPS% ^
181 | %MAVEN_OPTS% ^
182 | %MAVEN_DEBUG_OPTS% ^
183 | -classpath %WRAPPER_JAR% ^
184 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
185 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
186 | if ERRORLEVEL 1 goto error
187 | goto end
188 |
189 | :error
190 | set ERROR_CODE=1
191 |
192 | :end
193 | @endlocal & set ERROR_CODE=%ERROR_CODE%
194 |
195 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
196 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
197 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
198 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
199 | :skipRcPost
200 |
201 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
202 | if "%MAVEN_BATCH_PAUSE%"=="on" pause
203 |
204 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
205 |
206 | cmd /C exit /B %ERROR_CODE%
207 |
```