170 changed files with 70 additions and 12337 deletions
@ -1,12 +0,0 @@
@@ -1,12 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Grab("spring-boot-starter-actuator") |
||||
|
||||
@RestController |
||||
class SampleController { |
||||
|
||||
@RequestMapping("/") |
||||
public def hello() { |
||||
[message: "Hello World!"] |
||||
} |
||||
} |
||||
@ -1,23 +0,0 @@
@@ -1,23 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Component |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
private MyService myService |
||||
|
||||
void run(String... args) { |
||||
println "Hello ${this.myService.sayWorld()} From ${getClass().getClassLoader().getResource('samples/app.groovy')}" |
||||
} |
||||
} |
||||
|
||||
|
||||
@Service |
||||
class MyService { |
||||
|
||||
String sayWorld() { |
||||
return "World!" |
||||
} |
||||
} |
||||
|
||||
|
||||
@ -1,15 +0,0 @@
@@ -1,15 +0,0 @@
|
||||
@RestController |
||||
class Application { |
||||
|
||||
@Autowired |
||||
String foo |
||||
|
||||
@RequestMapping("/") |
||||
String home() { |
||||
"Hello ${foo}!" |
||||
} |
||||
} |
||||
|
||||
beans { |
||||
foo String, "World" |
||||
} |
||||
@ -1,47 +0,0 @@
@@ -1,47 +0,0 @@
|
||||
package org.test |
||||
|
||||
import java.util.concurrent.atomic.AtomicLong |
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableCaching |
||||
class Sample { |
||||
|
||||
@Bean CacheManager cacheManager() { |
||||
new ConcurrentMapCacheManager() |
||||
} |
||||
|
||||
@Component |
||||
static class MyClient implements CommandLineRunner { |
||||
|
||||
private final MyService myService |
||||
|
||||
@Autowired |
||||
MyClient(MyService myService) { |
||||
this.myService = myService |
||||
} |
||||
|
||||
void run(String... args) { |
||||
long counter = myService.get('someKey') |
||||
long counter2 = myService.get('someKey') |
||||
if (counter == counter2) { |
||||
println 'Hello World' |
||||
} else { |
||||
println 'Something went wrong with the cache setup' |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
@Component |
||||
static class MyService { |
||||
|
||||
private final AtomicLong counter = new AtomicLong() |
||||
|
||||
@Cacheable('foo') |
||||
Long get(String id) { |
||||
return counter.getAndIncrement() |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,23 +0,0 @@
@@ -1,23 +0,0 @@
|
||||
package org.test |
||||
|
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
@Controller |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
ApplicationContext context |
||||
|
||||
@RequestMapping("/") |
||||
@ResponseBody |
||||
public String helloWorld() { |
||||
return "World!" |
||||
} |
||||
|
||||
void run(String... args) { |
||||
def port = context.webServer.port |
||||
def world = new RestTemplate().getForObject("http://localhost:" + port + "/", String.class); |
||||
print "Hello " + world |
||||
} |
||||
|
||||
} |
||||
@ -1,39 +0,0 @@
@@ -1,39 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Configuration |
||||
@EnableIntegration |
||||
class SpringIntegrationExample implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
private ApplicationContext context |
||||
|
||||
@Bean |
||||
DirectChannel input() { |
||||
new DirectChannel() |
||||
} |
||||
|
||||
@Override |
||||
void run(String... args) { |
||||
println() |
||||
println '>>>> ' + new MessagingTemplate(input()).convertSendAndReceive("World", String) + ' <<<<' |
||||
println() |
||||
/* |
||||
* Since this is a simple application that we want to exit right away, |
||||
* close the context. For an active integration application, with pollers |
||||
* etc, you can either suspend the main thread here (e.g. with System.in.read()), |
||||
* or exit the run() method without closing the context, and stop the |
||||
* application later using some other technique (kill, JMX etc). |
||||
*/ |
||||
context.close() |
||||
} |
||||
} |
||||
|
||||
@MessageEndpoint |
||||
class HelloTransformer { |
||||
|
||||
@Transformer(inputChannel="input") |
||||
String transform(String payload) { |
||||
"Hello, ${payload}" |
||||
} |
||||
|
||||
} |
||||
@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Grab("spring-boot-starter-artemis") |
||||
@Grab("artemis-jakarta-server") |
||||
import java.util.concurrent.CountDownLatch |
||||
|
||||
@Log |
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableJms |
||||
class JmsExample implements CommandLineRunner { |
||||
|
||||
private CountDownLatch latch = new CountDownLatch(1) |
||||
|
||||
@Autowired |
||||
JmsTemplate jmsTemplate |
||||
|
||||
void run(String... args) { |
||||
def messageCreator = { session -> |
||||
session.createObjectMessage("Greetings from Spring Boot via Artemis") |
||||
} as MessageCreator |
||||
log.info "Sending JMS message..." |
||||
jmsTemplate.send("spring-boot", messageCreator) |
||||
log.info "Send JMS message, waiting..." |
||||
latch.await() |
||||
} |
||||
|
||||
@JmsListener(destination = 'spring-boot') |
||||
def receive(String message) { |
||||
log.info "Received ${message}" |
||||
latch.countDown() |
||||
} |
||||
|
||||
} |
||||
@ -1,38 +0,0 @@
@@ -1,38 +0,0 @@
|
||||
package org.test |
||||
|
||||
import org.springframework.transaction.TransactionManager |
||||
|
||||
@Grab("hsqldb") |
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableBatchProcessing |
||||
class JobConfig { |
||||
|
||||
@Autowired |
||||
private JobBuilderFactory jobs |
||||
|
||||
@Autowired |
||||
private StepBuilderFactory steps |
||||
|
||||
@Autowired |
||||
private TransactionManager transactionManager |
||||
|
||||
@Bean |
||||
protected Tasklet tasklet() { |
||||
return new Tasklet() { |
||||
@Override |
||||
RepeatStatus execute(StepContribution contribution, ChunkContext context) { |
||||
return RepeatStatus.FINISHED |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Bean |
||||
Job job() throws Exception { |
||||
return jobs.get("job").start(step1()).build() |
||||
} |
||||
|
||||
@Bean |
||||
protected Step step1() throws Exception { |
||||
return steps.get("step1").tasklet(tasklet()).transactionManager(this.transactionManager).build() |
||||
} |
||||
} |
||||
@ -1,32 +0,0 @@
@@ -1,32 +0,0 @@
|
||||
package org.test |
||||
|
||||
import java.util.concurrent.CountDownLatch |
||||
|
||||
@Log |
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableRabbit |
||||
class RabbitExample implements CommandLineRunner { |
||||
|
||||
private CountDownLatch latch = new CountDownLatch(1) |
||||
|
||||
@Autowired |
||||
RabbitTemplate rabbitTemplate |
||||
|
||||
void run(String... args) { |
||||
log.info "Sending RabbitMQ message..." |
||||
rabbitTemplate.convertAndSend("spring-boot", "Greetings from Spring Boot via RabbitMQ") |
||||
latch.await() |
||||
} |
||||
|
||||
@RabbitListener(queues = 'spring-boot') |
||||
def receive(String message) { |
||||
log.info "Received ${message}" |
||||
latch.countDown() |
||||
} |
||||
|
||||
@Bean |
||||
org.springframework.amqp.core.Queue queue() { |
||||
new org.springframework.amqp.core.Queue("spring-boot", false) |
||||
} |
||||
|
||||
} |
||||
@ -1,28 +0,0 @@
@@ -1,28 +0,0 @@
|
||||
package org.test |
||||
|
||||
@EnableRetry |
||||
@Component |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
private MyService myService |
||||
|
||||
void run(String... args) { |
||||
println "Hello ${this.myService.sayWorld()} From ${getClass().getClassLoader().getResource('samples/retry.groovy')}" |
||||
} |
||||
} |
||||
|
||||
|
||||
@Service |
||||
class MyService { |
||||
|
||||
static int count = 0 |
||||
|
||||
@Retryable |
||||
String sayWorld() { |
||||
if (count++==0) { |
||||
throw new IllegalStateException("Planned") |
||||
} |
||||
return "World!" |
||||
} |
||||
} |
||||
@ -1,8 +0,0 @@
@@ -1,8 +0,0 @@
|
||||
package org.test |
||||
|
||||
class Runner implements CommandLineRunner { |
||||
|
||||
void run(String... args) { |
||||
print "Hello World!" |
||||
} |
||||
} |
||||
@ -1,9 +0,0 @@
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> |
||||
|
||||
<bean id="runner" class="org.test.Runner"/> |
||||
|
||||
</beans> |
||||
|
||||
@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Grab("spring-boot-starter-security") |
||||
@Grab("spring-boot-starter-actuator") |
||||
|
||||
@RestController |
||||
class SampleController { |
||||
|
||||
@RequestMapping("/") |
||||
public def hello() { |
||||
[message: "Hello World!"] |
||||
} |
||||
} |
||||
@ -1,24 +0,0 @@
@@ -1,24 +0,0 @@
|
||||
package org.test |
||||
|
||||
import static org.springframework.boot.groovy.GroovyTemplate.* |
||||
|
||||
@Component |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
private MyService myService |
||||
|
||||
@Override |
||||
void run(String... args) { |
||||
print template("test.txt", ["message":myService.sayWorld()]) |
||||
} |
||||
} |
||||
|
||||
|
||||
@Service |
||||
class MyService { |
||||
|
||||
String sayWorld() { |
||||
return "World" |
||||
} |
||||
} |
||||
@ -1,17 +0,0 @@
@@ -1,17 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Grab("hsqldb") |
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableTransactionManagement |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
JdbcTemplate jdbcTemplate |
||||
|
||||
@Transactional |
||||
void run(String... args) { |
||||
println "Foo count=" + jdbcTemplate.queryForObject("SELECT COUNT(*) from FOO", Integer) |
||||
} |
||||
} |
||||
|
||||
@ -1,12 +0,0 @@
@@ -1,12 +0,0 @@
|
||||
package app |
||||
|
||||
@Grab("thymeleaf-spring6") |
||||
@Controller |
||||
class Example { |
||||
|
||||
@RequestMapping("/") |
||||
public String helloWorld(Map<String,Object> model) { |
||||
model.putAll([title: "My Page", date: new Date(), message: "Hello World"]) |
||||
return "home" |
||||
} |
||||
} |
||||
@ -1,21 +0,0 @@
@@ -1,21 +0,0 @@
|
||||
@Controller |
||||
class Example { |
||||
|
||||
@Autowired |
||||
private MyService myService |
||||
|
||||
@RequestMapping("/") |
||||
@ResponseBody |
||||
public String helloWorld() { |
||||
return myService.sayWorld() |
||||
} |
||||
|
||||
} |
||||
|
||||
@Service |
||||
class MyService { |
||||
|
||||
public String sayWorld() { |
||||
return "World!" |
||||
} |
||||
} |
||||
@ -1,150 +0,0 @@
@@ -1,150 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli; |
||||
|
||||
import java.io.File; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.io.TempDir; |
||||
|
||||
import org.springframework.boot.cli.command.archive.JarCommand; |
||||
import org.springframework.boot.cli.infrastructure.CommandLineInvoker; |
||||
import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation; |
||||
import org.springframework.boot.loader.tools.JavaExecutable; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test for {@link JarCommand}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
class JarCommandIT { |
||||
|
||||
private static final boolean JAVA_9_OR_LATER = isClassPresent("java.security.cert.URICertStoreParameters"); |
||||
|
||||
private CommandLineInvoker cli; |
||||
|
||||
private File tempDir; |
||||
|
||||
@BeforeEach |
||||
void setup(@TempDir File tempDir) { |
||||
this.cli = new CommandLineInvoker(new File("src/intTest/resources/jar-command"), tempDir); |
||||
this.tempDir = tempDir; |
||||
} |
||||
|
||||
@Test |
||||
void noArguments() throws Exception { |
||||
Invocation invocation = this.cli.invoke("jar"); |
||||
invocation.await(); |
||||
assertThat(invocation.getStandardOutput()).isEqualTo(""); |
||||
assertThat(invocation.getErrorOutput()) |
||||
.contains("The name of the resulting jar and at least one source file must be specified"); |
||||
} |
||||
|
||||
@Test |
||||
void noSources() throws Exception { |
||||
Invocation invocation = this.cli.invoke("jar", "test-app.jar"); |
||||
invocation.await(); |
||||
assertThat(invocation.getStandardOutput()).isEqualTo(""); |
||||
assertThat(invocation.getErrorOutput()) |
||||
.contains("The name of the resulting jar and at least one source file must be specified"); |
||||
} |
||||
|
||||
@Test |
||||
void jarCreationWithGrabResolver() throws Exception { |
||||
File jar = new File(this.tempDir, "test-app.jar"); |
||||
Invocation invocation = this.cli.invoke("run", jar.getAbsolutePath(), "bad.groovy"); |
||||
invocation.await(); |
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEqualTo(""); |
||||
} |
||||
invocation = this.cli.invoke("jar", jar.getAbsolutePath(), "bad.groovy"); |
||||
invocation.await(); |
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEmpty(); |
||||
} |
||||
assertThat(jar).exists(); |
||||
|
||||
Process process = new JavaExecutable().processBuilder("-jar", jar.getAbsolutePath()).start(); |
||||
invocation = new Invocation(process); |
||||
invocation.await(); |
||||
|
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEqualTo(""); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
void jarCreation() throws Exception { |
||||
File jar = new File(this.tempDir, "test-app.jar"); |
||||
Invocation invocation = this.cli.invoke("jar", jar.getAbsolutePath(), "jar.groovy"); |
||||
invocation.await(); |
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEmpty(); |
||||
} |
||||
assertThat(jar).exists(); |
||||
|
||||
Process process = new JavaExecutable().processBuilder("-jar", jar.getAbsolutePath()).start(); |
||||
invocation = new Invocation(process); |
||||
invocation.await(); |
||||
|
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEqualTo(""); |
||||
} |
||||
assertThat(invocation.getStandardOutput()).contains("Hello World!") |
||||
.contains("/BOOT-INF/classes!/public/public.txt").contains("/BOOT-INF/classes!/resources/resource.txt") |
||||
.contains("/BOOT-INF/classes!/static/static.txt").contains("/BOOT-INF/classes!/templates/template.txt") |
||||
.contains("/BOOT-INF/classes!/root.properties").contains("Goodbye Mama"); |
||||
} |
||||
|
||||
@Test |
||||
void jarCreationWithIncludes() throws Exception { |
||||
File jar = new File(this.tempDir, "test-app.jar"); |
||||
Invocation invocation = this.cli.invoke("jar", jar.getAbsolutePath(), "--include", "-public/**,-resources/**", |
||||
"jar.groovy"); |
||||
invocation.await(); |
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEmpty(); |
||||
} |
||||
assertThat(jar).exists(); |
||||
|
||||
Process process = new JavaExecutable().processBuilder("-jar", jar.getAbsolutePath()).start(); |
||||
invocation = new Invocation(process); |
||||
invocation.await(); |
||||
|
||||
if (!JAVA_9_OR_LATER) { |
||||
assertThat(invocation.getErrorOutput()).isEqualTo(""); |
||||
} |
||||
assertThat(invocation.getStandardOutput()).contains("Hello World!").doesNotContain("/public/public.txt") |
||||
.doesNotContain("/resources/resource.txt").contains("/static/static.txt") |
||||
.contains("/templates/template.txt").contains("Goodbye Mama"); |
||||
} |
||||
|
||||
private static boolean isClassPresent(String name) { |
||||
try { |
||||
Class.forName(name); |
||||
return true; |
||||
} |
||||
catch (Exception ex) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,66 +0,0 @@
@@ -1,66 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli; |
||||
|
||||
import java.io.File; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.io.TempDir; |
||||
|
||||
import org.springframework.boot.cli.command.archive.WarCommand; |
||||
import org.springframework.boot.cli.infrastructure.CommandLineInvoker; |
||||
import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation; |
||||
import org.springframework.boot.loader.tools.JavaExecutable; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test for {@link WarCommand}. |
||||
* |
||||
* @author Andrey Stolyarov |
||||
* @author Henri Kerola |
||||
*/ |
||||
class WarCommandIT { |
||||
|
||||
private CommandLineInvoker cli; |
||||
|
||||
private File tempDir; |
||||
|
||||
@BeforeEach |
||||
void setup(@TempDir File tempDir) { |
||||
this.cli = new CommandLineInvoker(new File("src/intTest/resources/war-command"), tempDir); |
||||
this.tempDir = tempDir; |
||||
} |
||||
|
||||
@Test |
||||
void warCreation() throws Exception { |
||||
File war = new File(this.tempDir, "test-app.war"); |
||||
Invocation invocation = this.cli.invoke("war", war.getAbsolutePath(), "war.groovy"); |
||||
invocation.await(); |
||||
assertThat(war.exists()).isTrue(); |
||||
Process process = new JavaExecutable().processBuilder("-jar", war.getAbsolutePath(), "--server.port=0").start(); |
||||
invocation = new Invocation(process); |
||||
invocation.await(); |
||||
assertThat(invocation.getOutput()).contains("onStart error"); |
||||
assertThat(invocation.getOutput()).contains("Tomcat started"); |
||||
assertThat(invocation.getOutput()).contains("/WEB-INF/lib-provided/tomcat-embed-core"); |
||||
assertThat(invocation.getOutput()).contains("WEB-INF/classes!/root.properties"); |
||||
process.destroy(); |
||||
} |
||||
|
||||
} |
||||
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
@GrabResolver(name='clojars.org', root='https://clojars.org/repo') |
||||
@Grab('redis.embedded:embedded-redis:0.2') |
||||
|
||||
@Component |
||||
class EmbeddedRedis { |
||||
} |
||||
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
package org.test |
||||
|
||||
@EnableGroovyTemplates |
||||
@Component |
||||
class Example implements CommandLineRunner { |
||||
|
||||
@Autowired |
||||
private MyService myService |
||||
|
||||
void run(String... args) { |
||||
println "Hello ${this.myService.sayWorld()}" |
||||
println getClass().getResource('/public/public.txt') |
||||
println getClass().getResource('/resources/resource.txt') |
||||
println getClass().getResource('/static/static.txt') |
||||
println getClass().getResource('/templates/template.txt') |
||||
println getClass().getResource('/root.properties') |
||||
println template('template.txt', [world:'Mama']) |
||||
} |
||||
} |
||||
|
||||
@Service |
||||
class MyService { |
||||
|
||||
String sayWorld() { |
||||
return 'World!' |
||||
} |
||||
} |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
Goodbye ${world} |
||||
@ -1,10 +0,0 @@
@@ -1,10 +0,0 @@
|
||||
package org.test |
||||
|
||||
@Component |
||||
class Example implements CommandLineRunner { |
||||
|
||||
void run(String... args) { |
||||
print "Ssshh" |
||||
} |
||||
|
||||
} |
||||
@ -1,17 +0,0 @@
@@ -1,17 +0,0 @@
|
||||
package org.test |
||||
|
||||
@RestController |
||||
class WarExample implements CommandLineRunner { |
||||
|
||||
@RequestMapping("/") |
||||
public String hello() { |
||||
return "Hello" |
||||
} |
||||
|
||||
void run(String... args) { |
||||
println getClass().getResource('/org/apache/tomcat/InstanceManager.class') |
||||
println getClass().getResource('/root.properties') |
||||
throw new RuntimeException("onStart error") |
||||
} |
||||
|
||||
} |
||||
@ -1,86 +0,0 @@
@@ -1,86 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.app; |
||||
|
||||
import java.lang.reflect.Constructor; |
||||
import java.lang.reflect.Method; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* A launcher for {@code SpringApplication} or a {@code SpringApplication} subclass. The |
||||
* class that is used can be configured using the System property |
||||
* {@code spring.application.class.name} or the {@code SPRING_APPLICATION_CLASS_NAME} |
||||
* environment variable. Uses reflection to allow the launching code to exist in a |
||||
* separate ClassLoader from the application code. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.2.0 |
||||
* @see System#getProperty(String) |
||||
* @see System#getenv(String) |
||||
*/ |
||||
public class SpringApplicationLauncher { |
||||
|
||||
private static final String DEFAULT_SPRING_APPLICATION_CLASS = "org.springframework.boot.SpringApplication"; |
||||
|
||||
private final ClassLoader classLoader; |
||||
|
||||
/** |
||||
* Creates a new launcher that will use the given {@code classLoader} to load the |
||||
* configured {@code SpringApplication} class. |
||||
* @param classLoader the {@code ClassLoader} to use |
||||
*/ |
||||
public SpringApplicationLauncher(ClassLoader classLoader) { |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
/** |
||||
* Launches the application created using the given {@code sources}. The application |
||||
* is launched with the given {@code args}. |
||||
* @param sources the sources for the application |
||||
* @param args the args for the application |
||||
* @return the application's {@code ApplicationContext} |
||||
* @throws Exception if the launch fails |
||||
*/ |
||||
public Object launch(Class<?>[] sources, String[] args) throws Exception { |
||||
Map<String, Object> defaultProperties = new HashMap<>(); |
||||
defaultProperties.put("spring.groovy.template.check-template-location", "false"); |
||||
Class<?> applicationClass = Class.forName(getSpringApplicationClassName(), false, this.classLoader); |
||||
Constructor<?> constructor = applicationClass.getDeclaredConstructor(Class[].class); |
||||
constructor.setAccessible(true); |
||||
Object application = constructor.newInstance((Object) sources); |
||||
applicationClass.getMethod("setDefaultProperties", Map.class).invoke(application, defaultProperties); |
||||
Method method = applicationClass.getMethod("run", String[].class); |
||||
return method.invoke(application, (Object) args); |
||||
} |
||||
|
||||
private String getSpringApplicationClassName() { |
||||
String className = System.getProperty("spring.application.class.name"); |
||||
if (className == null) { |
||||
className = getEnvironmentVariable("SPRING_APPLICATION_CLASS_NAME"); |
||||
} |
||||
if (className == null) { |
||||
className = DEFAULT_SPRING_APPLICATION_CLASS; |
||||
} |
||||
return className; |
||||
} |
||||
|
||||
protected String getEnvironmentVariable(String name) { |
||||
return System.getenv(name); |
||||
} |
||||
|
||||
} |
||||
@ -1,84 +0,0 @@
@@ -1,84 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.app; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.jar.Manifest; |
||||
|
||||
import jakarta.servlet.ServletContext; |
||||
import jakarta.servlet.ServletException; |
||||
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; |
||||
|
||||
/** |
||||
* {@link SpringBootServletInitializer} for CLI packaged WAR files. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class SpringApplicationWebApplicationInitializer extends SpringBootServletInitializer { |
||||
|
||||
/** |
||||
* The entry containing the source class. |
||||
*/ |
||||
public static final String SOURCE_ENTRY = "Spring-Application-Source-Classes"; |
||||
|
||||
private String[] sources; |
||||
|
||||
@Override |
||||
public void onStartup(ServletContext servletContext) throws ServletException { |
||||
try { |
||||
this.sources = getSources(servletContext); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
super.onStartup(servletContext); |
||||
} |
||||
|
||||
private String[] getSources(ServletContext servletContext) throws IOException { |
||||
Manifest manifest = getManifest(servletContext); |
||||
if (manifest == null) { |
||||
throw new IllegalStateException("Unable to read manifest"); |
||||
} |
||||
String sources = manifest.getMainAttributes().getValue(SOURCE_ENTRY); |
||||
return sources.split(","); |
||||
} |
||||
|
||||
private Manifest getManifest(ServletContext servletContext) throws IOException { |
||||
InputStream stream = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF"); |
||||
return (stream != null) ? new Manifest(stream) : null; |
||||
} |
||||
|
||||
@Override |
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { |
||||
try { |
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
||||
Class<?>[] sourceClasses = new Class<?>[this.sources.length]; |
||||
for (int i = 0; i < this.sources.length; i++) { |
||||
sourceClasses[i] = Class.forName(this.sources[i], false, classLoader); |
||||
} |
||||
return builder.sources(sourceClasses).properties("spring.groovy.template.check-template-location=false"); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Support classes for CLI applications. |
||||
*/ |
||||
package org.springframework.boot.cli.app; |
||||
@ -1,85 +0,0 @@
@@ -1,85 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.archive; |
||||
|
||||
import java.net.URL; |
||||
import java.net.URLClassLoader; |
||||
import java.util.Enumeration; |
||||
import java.util.jar.Attributes; |
||||
import java.util.jar.Manifest; |
||||
|
||||
import org.springframework.boot.cli.app.SpringApplicationLauncher; |
||||
|
||||
/** |
||||
* A launcher for a CLI application that has been compiled and packaged as a jar file. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public final class PackagedSpringApplicationLauncher { |
||||
|
||||
/** |
||||
* The entry containing the source class. |
||||
*/ |
||||
public static final String SOURCE_ENTRY = "Spring-Application-Source-Classes"; |
||||
|
||||
/** |
||||
* The entry containing the start class. |
||||
*/ |
||||
public static final String START_CLASS_ENTRY = "Start-Class"; |
||||
|
||||
private PackagedSpringApplicationLauncher() { |
||||
} |
||||
|
||||
private void run(String[] args) throws Exception { |
||||
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); |
||||
new SpringApplicationLauncher(classLoader).launch(getSources(classLoader), args); |
||||
} |
||||
|
||||
private Class<?>[] getSources(URLClassLoader classLoader) throws Exception { |
||||
Enumeration<URL> urls = classLoader.getResources("META-INF/MANIFEST.MF"); |
||||
while (urls.hasMoreElements()) { |
||||
URL url = urls.nextElement(); |
||||
Manifest manifest = new Manifest(url.openStream()); |
||||
if (isCliPackaged(manifest)) { |
||||
String sources = manifest.getMainAttributes().getValue(SOURCE_ENTRY); |
||||
return loadClasses(classLoader, sources.split(",")); |
||||
} |
||||
} |
||||
throw new IllegalStateException("Cannot locate " + SOURCE_ENTRY + " in MANIFEST.MF"); |
||||
} |
||||
|
||||
private boolean isCliPackaged(Manifest manifest) { |
||||
Attributes attributes = manifest.getMainAttributes(); |
||||
String startClass = attributes.getValue(START_CLASS_ENTRY); |
||||
return getClass().getName().equals(startClass); |
||||
} |
||||
|
||||
private Class<?>[] loadClasses(ClassLoader classLoader, String[] names) throws ClassNotFoundException { |
||||
Class<?>[] classes = new Class<?>[names.length]; |
||||
for (int i = 0; i < names.length; i++) { |
||||
classes[i] = Class.forName(names[i], false, classLoader); |
||||
} |
||||
return classes; |
||||
} |
||||
|
||||
public static void main(String[] args) throws Exception { |
||||
new PackagedSpringApplicationLauncher().run(args); |
||||
} |
||||
|
||||
} |
||||
@ -1,21 +0,0 @@
@@ -1,21 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Class that are packaged as part of CLI generated JARs. |
||||
* @see org.springframework.boot.cli.command.archive.JarCommand |
||||
*/ |
||||
package org.springframework.boot.cli.archive; |
||||
@ -1,317 +0,0 @@
@@ -1,317 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.archive; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.URI; |
||||
import java.net.URISyntaxException; |
||||
import java.net.URL; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
import java.util.jar.Manifest; |
||||
|
||||
import groovy.lang.Grab; |
||||
import joptsimple.OptionSet; |
||||
import joptsimple.OptionSpec; |
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.AnnotatedNode; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.ModuleNode; |
||||
import org.codehaus.groovy.ast.expr.ConstantExpression; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
import org.springframework.boot.cli.app.SpringApplicationLauncher; |
||||
import org.springframework.boot.cli.archive.PackagedSpringApplicationLauncher; |
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.cli.command.OptionParsingCommand; |
||||
import org.springframework.boot.cli.command.archive.ResourceMatcher.MatchedResource; |
||||
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
||||
import org.springframework.boot.cli.command.options.OptionHandler; |
||||
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.command.options.SourceOptions; |
||||
import org.springframework.boot.cli.command.status.ExitStatus; |
||||
import org.springframework.boot.cli.compiler.GroovyCompiler; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
import org.springframework.boot.loader.tools.JarWriter; |
||||
import org.springframework.boot.loader.tools.Layout; |
||||
import org.springframework.boot.loader.tools.Library; |
||||
import org.springframework.boot.loader.tools.LibraryScope; |
||||
import org.springframework.boot.loader.tools.Repackager; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Abstract {@link Command} to create a self-contained executable archive file from a CLI |
||||
* application. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Phillip Webb |
||||
* @author Andrey Stolyarov |
||||
* @author Henri Kerola |
||||
*/ |
||||
abstract class ArchiveCommand extends OptionParsingCommand { |
||||
|
||||
protected ArchiveCommand(String name, String description, OptionHandler optionHandler) { |
||||
super(name, description, optionHandler); |
||||
} |
||||
|
||||
@Override |
||||
public String getUsageHelp() { |
||||
return "[options] <" + getName() + "-name> <files>"; |
||||
} |
||||
|
||||
/** |
||||
* Abstract base {@link CompilerOptionHandler} for archive commands. |
||||
*/ |
||||
protected abstract static class ArchiveOptionHandler extends CompilerOptionHandler { |
||||
|
||||
private final String type; |
||||
|
||||
private final Layout layout; |
||||
|
||||
private OptionSpec<String> includeOption; |
||||
|
||||
private OptionSpec<String> excludeOption; |
||||
|
||||
public ArchiveOptionHandler(String type, Layout layout) { |
||||
this.type = type; |
||||
this.layout = layout; |
||||
} |
||||
|
||||
protected Layout getLayout() { |
||||
return this.layout; |
||||
} |
||||
|
||||
@Override |
||||
protected void doOptions() { |
||||
this.includeOption = option("include", |
||||
"Pattern applied to directories on the classpath to find files to include in the resulting ") |
||||
.withRequiredArg().withValuesSeparatedBy(",").defaultsTo(""); |
||||
this.excludeOption = option("exclude", "Pattern applied to directories on the classpath to find files to " |
||||
+ "exclude from the resulting " + this.type).withRequiredArg().withValuesSeparatedBy(",") |
||||
.defaultsTo(""); |
||||
} |
||||
|
||||
@Override |
||||
protected ExitStatus run(OptionSet options) throws Exception { |
||||
List<?> nonOptionArguments = new ArrayList<Object>(options.nonOptionArguments()); |
||||
Assert.isTrue(nonOptionArguments.size() >= 2, |
||||
() -> "The name of the resulting " + this.type + " and at least one source file must be specified"); |
||||
|
||||
File output = new File((String) nonOptionArguments.remove(0)); |
||||
Assert.isTrue(output.getName().toLowerCase(Locale.ENGLISH).endsWith("." + this.type), |
||||
() -> "The output '" + output + "' is not a " + this.type.toUpperCase(Locale.ENGLISH) + " file."); |
||||
deleteIfExists(output); |
||||
|
||||
GroovyCompiler compiler = createCompiler(options); |
||||
|
||||
List<URL> classpath = getClassPathUrls(compiler); |
||||
List<MatchedResource> classpathEntries = findMatchingClasspathEntries(classpath, options); |
||||
|
||||
String[] sources = new SourceOptions(nonOptionArguments).getSourcesArray(); |
||||
Class<?>[] compiledClasses = compiler.compile(sources); |
||||
|
||||
List<URL> dependencies = getClassPathUrls(compiler); |
||||
dependencies.removeAll(classpath); |
||||
|
||||
writeJar(output, compiledClasses, classpathEntries, dependencies); |
||||
return ExitStatus.OK; |
||||
} |
||||
|
||||
private void deleteIfExists(File file) { |
||||
if (file.exists() && !file.delete()) { |
||||
throw new IllegalStateException("Failed to delete existing file " + file.getPath()); |
||||
} |
||||
} |
||||
|
||||
private GroovyCompiler createCompiler(OptionSet options) { |
||||
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory |
||||
.createDefaultRepositoryConfiguration(); |
||||
GroovyCompilerConfiguration configuration = new OptionSetGroovyCompilerConfiguration(options, this, |
||||
repositoryConfiguration); |
||||
GroovyCompiler groovyCompiler = new GroovyCompiler(configuration); |
||||
groovyCompiler.getAstTransformations().add(0, new GrabAnnotationTransform()); |
||||
return groovyCompiler; |
||||
} |
||||
|
||||
private List<URL> getClassPathUrls(GroovyCompiler compiler) { |
||||
return new ArrayList<>(Arrays.asList(compiler.getLoader().getURLs())); |
||||
} |
||||
|
||||
private List<MatchedResource> findMatchingClasspathEntries(List<URL> classpath, OptionSet options) |
||||
throws IOException { |
||||
ResourceMatcher matcher = new ResourceMatcher(options.valuesOf(this.includeOption), |
||||
options.valuesOf(this.excludeOption)); |
||||
List<File> roots = new ArrayList<>(); |
||||
for (URL classpathEntry : classpath) { |
||||
roots.add(new File(URI.create(classpathEntry.toString()))); |
||||
} |
||||
return matcher.find(roots); |
||||
} |
||||
|
||||
private void writeJar(File file, Class<?>[] compiledClasses, List<MatchedResource> classpathEntries, |
||||
List<URL> dependencies) throws IOException, URISyntaxException { |
||||
final List<Library> libraries; |
||||
try (JarWriter writer = new JarWriter(file)) { |
||||
addManifest(writer, compiledClasses); |
||||
addCliClasses(writer); |
||||
for (Class<?> compiledClass : compiledClasses) { |
||||
addClass(writer, compiledClass); |
||||
} |
||||
libraries = addClasspathEntries(writer, classpathEntries); |
||||
} |
||||
libraries.addAll(createLibraries(dependencies)); |
||||
Repackager repackager = new Repackager(file); |
||||
repackager.setMainClass(PackagedSpringApplicationLauncher.class.getName()); |
||||
repackager.repackage((callback) -> { |
||||
for (Library library : libraries) { |
||||
callback.library(library); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private List<Library> createLibraries(List<URL> dependencies) throws URISyntaxException { |
||||
List<Library> libraries = new ArrayList<>(); |
||||
for (URL dependency : dependencies) { |
||||
File file = new File(dependency.toURI()); |
||||
libraries.add(new Library(null, file, getLibraryScope(file), null, false, false, true)); |
||||
} |
||||
return libraries; |
||||
} |
||||
|
||||
private void addManifest(JarWriter writer, Class<?>[] compiledClasses) throws IOException { |
||||
Manifest manifest = new Manifest(); |
||||
manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); |
||||
manifest.getMainAttributes().putValue(PackagedSpringApplicationLauncher.SOURCE_ENTRY, |
||||
commaDelimitedClassNames(compiledClasses)); |
||||
writer.writeManifest(manifest); |
||||
} |
||||
|
||||
private String commaDelimitedClassNames(Class<?>[] classes) { |
||||
StringBuilder builder = new StringBuilder(); |
||||
for (int i = 0; i < classes.length; i++) { |
||||
if (i != 0) { |
||||
builder.append(','); |
||||
} |
||||
builder.append(classes[i].getName()); |
||||
} |
||||
return builder.toString(); |
||||
} |
||||
|
||||
protected void addCliClasses(JarWriter writer) throws IOException { |
||||
addClass(writer, PackagedSpringApplicationLauncher.class); |
||||
addClass(writer, SpringApplicationLauncher.class); |
||||
Resource[] resources = new PathMatchingResourcePatternResolver() |
||||
.getResources("org/springframework/boot/groovy/**"); |
||||
for (Resource resource : resources) { |
||||
String url = resource.getURL().toString(); |
||||
addResource(writer, resource, url.substring(url.indexOf("org/springframework/boot/groovy/"))); |
||||
} |
||||
} |
||||
|
||||
protected final void addClass(JarWriter writer, Class<?> sourceClass) throws IOException { |
||||
addClass(writer, sourceClass.getClassLoader(), sourceClass.getName()); |
||||
} |
||||
|
||||
protected final void addClass(JarWriter writer, ClassLoader classLoader, String sourceClass) |
||||
throws IOException { |
||||
if (classLoader == null) { |
||||
classLoader = Thread.currentThread().getContextClassLoader(); |
||||
} |
||||
String name = sourceClass.replace('.', '/') + ".class"; |
||||
InputStream stream = classLoader.getResourceAsStream(name); |
||||
writer.writeEntry(this.layout.getClassesLocation() + name, stream); |
||||
} |
||||
|
||||
private void addResource(JarWriter writer, Resource resource, String name) throws IOException { |
||||
InputStream stream = resource.getInputStream(); |
||||
writer.writeEntry(name, stream); |
||||
} |
||||
|
||||
private List<Library> addClasspathEntries(JarWriter writer, List<MatchedResource> entries) throws IOException { |
||||
List<Library> libraries = new ArrayList<>(); |
||||
for (MatchedResource entry : entries) { |
||||
if (entry.isRoot()) { |
||||
libraries.add(new Library(null, entry.getFile(), LibraryScope.COMPILE, null, false, false, true)); |
||||
} |
||||
else { |
||||
writeClasspathEntry(writer, entry); |
||||
} |
||||
} |
||||
return libraries; |
||||
} |
||||
|
||||
protected void writeClasspathEntry(JarWriter writer, MatchedResource entry) throws IOException { |
||||
writer.writeEntry(entry.getName(), new FileInputStream(entry.getFile())); |
||||
} |
||||
|
||||
protected abstract LibraryScope getLibraryScope(File file); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* {@link ASTTransformation} to change {@code @Grab} annotation values. |
||||
*/ |
||||
private static class GrabAnnotationTransform implements ASTTransformation { |
||||
|
||||
@Override |
||||
public void visit(ASTNode[] nodes, SourceUnit source) { |
||||
for (ASTNode node : nodes) { |
||||
if (node instanceof ModuleNode moduleNode) { |
||||
visitModule(moduleNode); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void visitModule(ModuleNode module) { |
||||
for (ClassNode classNode : module.getClasses()) { |
||||
AnnotationNode annotation = new AnnotationNode(new ClassNode(Grab.class)); |
||||
annotation.addMember("value", new ConstantExpression("groovy")); |
||||
classNode.addAnnotation(annotation); |
||||
// We only need to do it at most once
|
||||
break; |
||||
} |
||||
// Disable the addition of a static initializer that calls Grape.addResolver
|
||||
// because all the dependencies are local now
|
||||
disableGrabResolvers(module.getClasses()); |
||||
disableGrabResolvers(module.getImports()); |
||||
} |
||||
|
||||
private void disableGrabResolvers(List<? extends AnnotatedNode> nodes) { |
||||
for (AnnotatedNode classNode : nodes) { |
||||
List<AnnotationNode> annotations = classNode.getAnnotations(); |
||||
for (AnnotationNode node : new ArrayList<>(annotations)) { |
||||
if (node.getClassNode().getNameWithoutPackage().equals("GrabResolver")) { |
||||
node.setMember("initClass", new ConstantExpression(false)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,51 +0,0 @@
@@ -1,51 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.archive; |
||||
|
||||
import java.io.File; |
||||
|
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.loader.tools.Layouts; |
||||
import org.springframework.boot.loader.tools.LibraryScope; |
||||
|
||||
/** |
||||
* {@link Command} to create a self-contained executable jar file from a CLI application. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Phillip Webb |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class JarCommand extends ArchiveCommand { |
||||
|
||||
public JarCommand() { |
||||
super("jar", "Create a self-contained executable jar file from a Spring Groovy script", new JarOptionHandler()); |
||||
} |
||||
|
||||
private static final class JarOptionHandler extends ArchiveOptionHandler { |
||||
|
||||
JarOptionHandler() { |
||||
super("jar", new Layouts.Jar()); |
||||
} |
||||
|
||||
@Override |
||||
protected LibraryScope getLibraryScope(File file) { |
||||
return LibraryScope.COMPILE; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,222 +0,0 @@
@@ -1,222 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.archive; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.net.MalformedURLException; |
||||
import java.net.URL; |
||||
import java.net.URLClassLoader; |
||||
import java.util.ArrayList; |
||||
import java.util.Enumeration; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader; |
||||
import org.springframework.core.io.FileSystemResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.ResourceLoader; |
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; |
||||
import org.springframework.util.AntPathMatcher; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Used to match resources for inclusion in a CLI application's jar file. |
||||
* |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
class ResourceMatcher { |
||||
|
||||
private static final String[] DEFAULT_INCLUDES = { "public/**", "resources/**", "static/**", "templates/**", |
||||
"META-INF/**", "*" }; |
||||
|
||||
private static final String[] DEFAULT_EXCLUDES = { ".*", "repository/**", "build/**", "target/**", "**/*.jar", |
||||
"**/*.groovy" }; |
||||
|
||||
private final AntPathMatcher pathMatcher = new AntPathMatcher(); |
||||
|
||||
private final List<String> includes; |
||||
|
||||
private final List<String> excludes; |
||||
|
||||
ResourceMatcher(List<String> includes, List<String> excludes) { |
||||
this.includes = getOptions(includes, DEFAULT_INCLUDES); |
||||
this.excludes = getOptions(excludes, DEFAULT_EXCLUDES); |
||||
} |
||||
|
||||
List<MatchedResource> find(List<File> roots) throws IOException { |
||||
List<MatchedResource> matchedResources = new ArrayList<>(); |
||||
for (File root : roots) { |
||||
if (root.isFile()) { |
||||
matchedResources.add(new MatchedResource(root)); |
||||
} |
||||
else { |
||||
matchedResources.addAll(findInDirectory(root)); |
||||
} |
||||
} |
||||
return matchedResources; |
||||
} |
||||
|
||||
private List<MatchedResource> findInDirectory(File directory) throws IOException { |
||||
List<MatchedResource> matchedResources = new ArrayList<>(); |
||||
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver( |
||||
new DirectoryResourceLoader(directory)); |
||||
|
||||
for (String include : this.includes) { |
||||
for (Resource candidate : resolver.getResources(include)) { |
||||
File file = candidate.getFile(); |
||||
if (file.isFile()) { |
||||
MatchedResource matchedResource = new MatchedResource(directory, file); |
||||
if (!isExcluded(matchedResource)) { |
||||
matchedResources.add(matchedResource); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return matchedResources; |
||||
} |
||||
|
||||
private boolean isExcluded(MatchedResource matchedResource) { |
||||
for (String exclude : this.excludes) { |
||||
if (this.pathMatcher.match(exclude, matchedResource.getName())) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private List<String> getOptions(List<String> values, String[] defaults) { |
||||
Set<String> result = new LinkedHashSet<>(); |
||||
Set<String> minus = new LinkedHashSet<>(); |
||||
boolean deltasFound = false; |
||||
for (String value : values) { |
||||
if (value.startsWith("+")) { |
||||
deltasFound = true; |
||||
value = value.substring(1); |
||||
result.add(value); |
||||
} |
||||
else if (value.startsWith("-")) { |
||||
deltasFound = true; |
||||
value = value.substring(1); |
||||
minus.add(value); |
||||
} |
||||
else if (!value.trim().isEmpty()) { |
||||
result.add(value); |
||||
} |
||||
} |
||||
for (String value : defaults) { |
||||
if (!minus.contains(value) || !deltasFound) { |
||||
result.add(value); |
||||
} |
||||
} |
||||
return new ArrayList<>(result); |
||||
} |
||||
|
||||
/** |
||||
* {@link ResourceLoader} to get load resource from a directory. |
||||
*/ |
||||
private static class DirectoryResourceLoader extends DefaultResourceLoader { |
||||
|
||||
private final File rootDirectory; |
||||
|
||||
DirectoryResourceLoader(File root) throws MalformedURLException { |
||||
super(new DirectoryClassLoader(root)); |
||||
this.rootDirectory = root; |
||||
} |
||||
|
||||
@Override |
||||
protected Resource getResourceByPath(String path) { |
||||
return new FileSystemResource(new File(this.rootDirectory, path)); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* {@link ClassLoader} backed by a directory. |
||||
*/ |
||||
private static class DirectoryClassLoader extends URLClassLoader { |
||||
|
||||
DirectoryClassLoader(File rootDirectory) throws MalformedURLException { |
||||
super(new URL[] { rootDirectory.toURI().toURL() }); |
||||
} |
||||
|
||||
@Override |
||||
public Enumeration<URL> getResources(String name) throws IOException { |
||||
return findResources(name); |
||||
} |
||||
|
||||
@Override |
||||
public URL getResource(String name) { |
||||
return findResource(name); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* A single matched resource. |
||||
*/ |
||||
public static final class MatchedResource { |
||||
|
||||
private final File file; |
||||
|
||||
private final String name; |
||||
|
||||
private final boolean root; |
||||
|
||||
private MatchedResource(File file) { |
||||
this.name = file.getName(); |
||||
this.file = file; |
||||
this.root = this.name.endsWith(".jar"); |
||||
} |
||||
|
||||
private MatchedResource(File rootDirectory, File file) { |
||||
String filePath = file.getAbsolutePath(); |
||||
String rootDirectoryPath = rootDirectory.getAbsolutePath(); |
||||
this.name = StringUtils.cleanPath(filePath.substring(rootDirectoryPath.length() + 1)); |
||||
this.file = file; |
||||
this.root = false; |
||||
} |
||||
|
||||
private MatchedResource(File resourceFile, String path, boolean root) { |
||||
this.file = resourceFile; |
||||
this.name = path; |
||||
this.root = root; |
||||
} |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public File getFile() { |
||||
return this.file; |
||||
} |
||||
|
||||
public boolean isRoot() { |
||||
return this.root; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return this.file.getAbsolutePath(); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,70 +0,0 @@
@@ -1,70 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.archive; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
|
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.loader.tools.JarWriter; |
||||
import org.springframework.boot.loader.tools.Layouts; |
||||
import org.springframework.boot.loader.tools.LibraryScope; |
||||
|
||||
/** |
||||
* {@link Command} to create a self-contained executable jar file from a CLI application. |
||||
* |
||||
* @author Andrey Stolyarov |
||||
* @author Phillip Webb |
||||
* @author Henri Kerola |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class WarCommand extends ArchiveCommand { |
||||
|
||||
public WarCommand() { |
||||
super("war", "Create a self-contained executable war file from a Spring Groovy script", new WarOptionHandler()); |
||||
} |
||||
|
||||
private static final class WarOptionHandler extends ArchiveOptionHandler { |
||||
|
||||
WarOptionHandler() { |
||||
super("war", new Layouts.War()); |
||||
} |
||||
|
||||
@Override |
||||
protected LibraryScope getLibraryScope(File file) { |
||||
String fileName = file.getName(); |
||||
if (fileName.contains("tomcat-embed") || fileName.contains("spring-boot-starter-tomcat")) { |
||||
return LibraryScope.PROVIDED; |
||||
} |
||||
return LibraryScope.COMPILE; |
||||
} |
||||
|
||||
@Override |
||||
protected void addCliClasses(JarWriter writer) throws IOException { |
||||
addClass(writer, null, "org.springframework.boot.cli.app.SpringApplicationWebApplicationInitializer"); |
||||
super.addCliClasses(writer); |
||||
} |
||||
|
||||
@Override |
||||
protected void writeClasspathEntry(JarWriter writer, ResourceMatcher.MatchedResource entry) throws IOException { |
||||
writer.writeEntry(getLayout().getClassesLocation() + entry.getName(), new FileInputStream(entry.getFile())); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* CLI commands for creating jars and wars. |
||||
*/ |
||||
package org.springframework.boot.cli.command.archive; |
||||
@ -1,65 +0,0 @@
@@ -1,65 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.grab; |
||||
|
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
|
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.cli.command.OptionParsingCommand; |
||||
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
||||
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.command.options.SourceOptions; |
||||
import org.springframework.boot.cli.command.status.ExitStatus; |
||||
import org.springframework.boot.cli.compiler.GroovyCompiler; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
|
||||
/** |
||||
* {@link Command} to grab the dependencies of one or more Groovy scripts. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class GrabCommand extends OptionParsingCommand { |
||||
|
||||
public GrabCommand() { |
||||
super("grab", "Download a spring groovy script's dependencies to ./repository", new GrabOptionHandler()); |
||||
} |
||||
|
||||
private static final class GrabOptionHandler extends CompilerOptionHandler { |
||||
|
||||
@Override |
||||
protected ExitStatus run(OptionSet options) throws Exception { |
||||
SourceOptions sourceOptions = new SourceOptions(options); |
||||
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory |
||||
.createDefaultRepositoryConfiguration(); |
||||
GroovyCompilerConfiguration configuration = new OptionSetGroovyCompilerConfiguration(options, this, |
||||
repositoryConfiguration); |
||||
if (System.getProperty("grape.root") == null) { |
||||
System.setProperty("grape.root", "."); |
||||
} |
||||
GroovyCompiler groovyCompiler = new GroovyCompiler(configuration); |
||||
groovyCompiler.compile(sourceOptions.getSourcesArray()); |
||||
return ExitStatus.OK; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* CLI command for grabbing dependencies. |
||||
*/ |
||||
package org.springframework.boot.cli.command.grab; |
||||
@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.install; |
||||
|
||||
import java.io.File; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Resolve artifact identifiers (typically in the form {@literal group:artifact:version}) |
||||
* to {@link File}s. |
||||
* |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
@FunctionalInterface |
||||
interface DependencyResolver { |
||||
|
||||
/** |
||||
* Resolves the given {@code artifactIdentifiers}, typically in the form |
||||
* "group:artifact:version", and their dependencies. |
||||
* @param artifactIdentifiers the artifacts to resolve |
||||
* @return the {@code File}s for the resolved artifacts |
||||
* @throws Exception if dependency resolution fails |
||||
*/ |
||||
List<File> resolve(List<String> artifactIdentifiers) throws Exception; |
||||
|
||||
} |
||||
@ -1,92 +0,0 @@
@@ -1,92 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.install; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.OutputStreamWriter; |
||||
import java.net.URISyntaxException; |
||||
import java.net.URL; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
|
||||
import org.springframework.boot.cli.compiler.GroovyCompiler; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
|
||||
/** |
||||
* A {@code DependencyResolver} implemented using Groovy's {@code @Grab}. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
class GroovyGrabDependencyResolver implements DependencyResolver { |
||||
|
||||
private final GroovyCompilerConfiguration configuration; |
||||
|
||||
GroovyGrabDependencyResolver(GroovyCompilerConfiguration configuration) { |
||||
this.configuration = configuration; |
||||
} |
||||
|
||||
@Override |
||||
public List<File> resolve(List<String> artifactIdentifiers) throws CompilationFailedException, IOException { |
||||
GroovyCompiler groovyCompiler = new GroovyCompiler(this.configuration); |
||||
List<File> artifactFiles = new ArrayList<>(); |
||||
if (!artifactIdentifiers.isEmpty()) { |
||||
List<URL> initialUrls = getClassPathUrls(groovyCompiler); |
||||
groovyCompiler.compile(createSources(artifactIdentifiers)); |
||||
List<URL> artifactUrls = getClassPathUrls(groovyCompiler); |
||||
artifactUrls.removeAll(initialUrls); |
||||
for (URL artifactUrl : artifactUrls) { |
||||
artifactFiles.add(toFile(artifactUrl)); |
||||
} |
||||
} |
||||
return artifactFiles; |
||||
} |
||||
|
||||
private List<URL> getClassPathUrls(GroovyCompiler compiler) { |
||||
return new ArrayList<>(Arrays.asList(compiler.getLoader().getURLs())); |
||||
} |
||||
|
||||
private String createSources(List<String> artifactIdentifiers) throws IOException { |
||||
File file = File.createTempFile("SpringCLIDependency", ".groovy"); |
||||
file.deleteOnExit(); |
||||
try (OutputStreamWriter stream = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { |
||||
for (String artifactIdentifier : artifactIdentifiers) { |
||||
stream.write("@Grab('" + artifactIdentifier + "')"); |
||||
} |
||||
// Dummy class to force compiler to do grab
|
||||
stream.write("class Installer {}"); |
||||
} |
||||
// Windows paths get tricky unless you work with URI
|
||||
return file.getAbsoluteFile().toURI().toString(); |
||||
} |
||||
|
||||
private File toFile(URL url) { |
||||
try { |
||||
return new File(url.toURI()); |
||||
} |
||||
catch (URISyntaxException ex) { |
||||
return new File(url.getPath()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,68 +0,0 @@
@@ -1,68 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.install; |
||||
|
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
|
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.cli.command.OptionParsingCommand; |
||||
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
||||
import org.springframework.boot.cli.command.status.ExitStatus; |
||||
import org.springframework.boot.cli.util.Log; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* {@link Command} to install additional dependencies into the CLI. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Andy Wilkinson |
||||
* @since 1.2.0 |
||||
*/ |
||||
public class InstallCommand extends OptionParsingCommand { |
||||
|
||||
public InstallCommand() { |
||||
super("install", "Install dependencies to the lib/ext directory", new InstallOptionHandler()); |
||||
} |
||||
|
||||
@Override |
||||
public String getUsageHelp() { |
||||
return "[options] <coordinates>"; |
||||
} |
||||
|
||||
private static final class InstallOptionHandler extends CompilerOptionHandler { |
||||
|
||||
@Override |
||||
@SuppressWarnings("unchecked") |
||||
protected ExitStatus run(OptionSet options) throws Exception { |
||||
List<String> args = (List<String>) options.nonOptionArguments(); |
||||
Assert.notEmpty(args, |
||||
"Please specify at least one dependency, in the form group:artifact:version, to install"); |
||||
try { |
||||
new Installer(options, this).install(args); |
||||
} |
||||
catch (Exception ex) { |
||||
String message = ex.getMessage(); |
||||
Log.error((message != null) ? message : ex.getClass().toString()); |
||||
} |
||||
return ExitStatus.OK; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,155 +0,0 @@
@@ -1,155 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.install; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileReader; |
||||
import java.io.FileWriter; |
||||
import java.io.IOException; |
||||
import java.util.List; |
||||
import java.util.Properties; |
||||
|
||||
import joptsimple.OptionSet; |
||||
|
||||
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
||||
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
import org.springframework.boot.cli.util.Log; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.SystemPropertyUtils; |
||||
|
||||
/** |
||||
* Shared logic for the {@link InstallCommand} and {@link UninstallCommand}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
class Installer { |
||||
|
||||
private final DependencyResolver dependencyResolver; |
||||
|
||||
private final Properties installCounts; |
||||
|
||||
Installer(OptionSet options, CompilerOptionHandler compilerOptionHandler) throws IOException { |
||||
this(new GroovyGrabDependencyResolver(createCompilerConfiguration(options, compilerOptionHandler))); |
||||
} |
||||
|
||||
Installer(DependencyResolver resolver) throws IOException { |
||||
this.dependencyResolver = resolver; |
||||
this.installCounts = loadInstallCounts(); |
||||
} |
||||
|
||||
private static GroovyCompilerConfiguration createCompilerConfiguration(OptionSet options, |
||||
CompilerOptionHandler compilerOptionHandler) { |
||||
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory |
||||
.createDefaultRepositoryConfiguration(); |
||||
return new OptionSetGroovyCompilerConfiguration(options, compilerOptionHandler, repositoryConfiguration) { |
||||
@Override |
||||
public boolean isAutoconfigure() { |
||||
return false; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
private Properties loadInstallCounts() throws IOException { |
||||
Properties properties = new Properties(); |
||||
File installed = getInstalled(); |
||||
if (installed.exists()) { |
||||
FileReader reader = new FileReader(installed); |
||||
properties.load(reader); |
||||
reader.close(); |
||||
} |
||||
return properties; |
||||
} |
||||
|
||||
private void saveInstallCounts() throws IOException { |
||||
try (FileWriter writer = new FileWriter(getInstalled())) { |
||||
this.installCounts.store(writer, null); |
||||
} |
||||
} |
||||
|
||||
void install(List<String> artifactIdentifiers) throws Exception { |
||||
File extDirectory = getDefaultExtDirectory(); |
||||
extDirectory.mkdirs(); |
||||
Log.info("Installing into: " + extDirectory); |
||||
List<File> artifactFiles = this.dependencyResolver.resolve(artifactIdentifiers); |
||||
for (File artifactFile : artifactFiles) { |
||||
int installCount = getInstallCount(artifactFile); |
||||
if (installCount == 0) { |
||||
FileCopyUtils.copy(artifactFile, new File(extDirectory, artifactFile.getName())); |
||||
} |
||||
setInstallCount(artifactFile, installCount + 1); |
||||
} |
||||
saveInstallCounts(); |
||||
} |
||||
|
||||
private int getInstallCount(File file) { |
||||
String countString = this.installCounts.getProperty(file.getName()); |
||||
if (countString == null) { |
||||
return 0; |
||||
} |
||||
return Integer.parseInt(countString); |
||||
} |
||||
|
||||
private void setInstallCount(File file, int count) { |
||||
if (count == 0) { |
||||
this.installCounts.remove(file.getName()); |
||||
} |
||||
else { |
||||
this.installCounts.setProperty(file.getName(), Integer.toString(count)); |
||||
} |
||||
} |
||||
|
||||
void uninstall(List<String> artifactIdentifiers) throws Exception { |
||||
File extDirectory = getDefaultExtDirectory(); |
||||
Log.info("Uninstalling from: " + extDirectory); |
||||
List<File> artifactFiles = this.dependencyResolver.resolve(artifactIdentifiers); |
||||
for (File artifactFile : artifactFiles) { |
||||
int installCount = getInstallCount(artifactFile); |
||||
if (installCount <= 1) { |
||||
new File(extDirectory, artifactFile.getName()).delete(); |
||||
} |
||||
setInstallCount(artifactFile, installCount - 1); |
||||
} |
||||
saveInstallCounts(); |
||||
} |
||||
|
||||
void uninstallAll() throws Exception { |
||||
File extDirectory = getDefaultExtDirectory(); |
||||
Log.info("Uninstalling from: " + extDirectory); |
||||
for (String name : this.installCounts.stringPropertyNames()) { |
||||
new File(extDirectory, name).delete(); |
||||
} |
||||
this.installCounts.clear(); |
||||
saveInstallCounts(); |
||||
} |
||||
|
||||
private File getDefaultExtDirectory() { |
||||
String home = SystemPropertyUtils.resolvePlaceholders("${spring.home:${SPRING_HOME:.}}"); |
||||
File extDirectory = new File(new File(home, "lib"), "ext"); |
||||
if (!extDirectory.isDirectory() && !extDirectory.mkdirs()) { |
||||
throw new IllegalStateException("Failed to create ext directory " + extDirectory); |
||||
} |
||||
return extDirectory; |
||||
} |
||||
|
||||
private File getInstalled() { |
||||
return new File(getDefaultExtDirectory(), ".installed"); |
||||
} |
||||
|
||||
} |
||||
@ -1,83 +0,0 @@
@@ -1,83 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.install; |
||||
|
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
import joptsimple.OptionSpec; |
||||
|
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.cli.command.OptionParsingCommand; |
||||
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
||||
import org.springframework.boot.cli.command.status.ExitStatus; |
||||
import org.springframework.boot.cli.util.Log; |
||||
|
||||
/** |
||||
* {@link Command} to uninstall dependencies from the CLI's lib/ext directory. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Andy Wilkinson |
||||
* @since 1.2.0 |
||||
*/ |
||||
public class UninstallCommand extends OptionParsingCommand { |
||||
|
||||
public UninstallCommand() { |
||||
super("uninstall", "Uninstall dependencies from the lib/ext directory", new UninstallOptionHandler()); |
||||
} |
||||
|
||||
@Override |
||||
public String getUsageHelp() { |
||||
return "[options] <coordinates>"; |
||||
} |
||||
|
||||
private static class UninstallOptionHandler extends CompilerOptionHandler { |
||||
|
||||
private OptionSpec<Void> allOption; |
||||
|
||||
@Override |
||||
protected void doOptions() { |
||||
this.allOption = option("all", "Uninstall all"); |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("unchecked") |
||||
protected ExitStatus run(OptionSet options) throws Exception { |
||||
List<String> args = (List<String>) options.nonOptionArguments(); |
||||
try { |
||||
if (options.has(this.allOption)) { |
||||
if (!args.isEmpty()) { |
||||
throw new IllegalArgumentException("Please use --all without specifying any dependencies"); |
||||
} |
||||
new Installer(options, this).uninstallAll(); |
||||
} |
||||
if (args.isEmpty()) { |
||||
throw new IllegalArgumentException( |
||||
"Please specify at least one dependency, in the form group:artifact:version, to uninstall"); |
||||
} |
||||
new Installer(options, this).uninstall(args); |
||||
} |
||||
catch (Exception ex) { |
||||
String message = ex.getMessage(); |
||||
Log.error((message != null) ? message : ex.getClass().toString()); |
||||
} |
||||
return ExitStatus.OK; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* CLI commands for installing and uninstalling CLI dependencies. |
||||
*/ |
||||
package org.springframework.boot.cli.command.install; |
||||
@ -1,71 +0,0 @@
@@ -1,71 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.options; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import joptsimple.OptionSpec; |
||||
|
||||
/** |
||||
* An {@link OptionHandler} for commands that result in the compilation of one or more |
||||
* Groovy scripts. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class CompilerOptionHandler extends OptionHandler { |
||||
|
||||
private OptionSpec<Void> noGuessImportsOption; |
||||
|
||||
private OptionSpec<Void> noGuessDependenciesOption; |
||||
|
||||
private OptionSpec<Boolean> autoconfigureOption; |
||||
|
||||
private OptionSpec<String> classpathOption; |
||||
|
||||
@Override |
||||
protected final void options() { |
||||
this.noGuessImportsOption = option("no-guess-imports", "Do not attempt to guess imports"); |
||||
this.noGuessDependenciesOption = option("no-guess-dependencies", "Do not attempt to guess dependencies"); |
||||
this.autoconfigureOption = option("autoconfigure", "Add autoconfigure compiler transformations") |
||||
.withOptionalArg().ofType(Boolean.class).defaultsTo(true); |
||||
this.classpathOption = option(Arrays.asList("classpath", "cp"), "Additional classpath entries") |
||||
.withRequiredArg(); |
||||
doOptions(); |
||||
} |
||||
|
||||
protected void doOptions() { |
||||
} |
||||
|
||||
public OptionSpec<Void> getNoGuessImportsOption() { |
||||
return this.noGuessImportsOption; |
||||
} |
||||
|
||||
public OptionSpec<Void> getNoGuessDependenciesOption() { |
||||
return this.noGuessDependenciesOption; |
||||
} |
||||
|
||||
public OptionSpec<String> getClasspathOption() { |
||||
return this.classpathOption; |
||||
} |
||||
|
||||
public OptionSpec<Boolean> getAutoconfigureOption() { |
||||
return this.autoconfigureOption; |
||||
} |
||||
|
||||
} |
||||
@ -1,98 +0,0 @@
@@ -1,98 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.options; |
||||
|
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
import joptsimple.OptionSpec; |
||||
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerScope; |
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
|
||||
/** |
||||
* Simple adapter class to present an {@link OptionSet} as a |
||||
* {@link GroovyCompilerConfiguration}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class OptionSetGroovyCompilerConfiguration implements GroovyCompilerConfiguration { |
||||
|
||||
private final OptionSet options; |
||||
|
||||
private final CompilerOptionHandler optionHandler; |
||||
|
||||
private final List<RepositoryConfiguration> repositoryConfiguration; |
||||
|
||||
protected OptionSetGroovyCompilerConfiguration(OptionSet optionSet, CompilerOptionHandler compilerOptionHandler) { |
||||
this(optionSet, compilerOptionHandler, RepositoryConfigurationFactory.createDefaultRepositoryConfiguration()); |
||||
} |
||||
|
||||
public OptionSetGroovyCompilerConfiguration(OptionSet optionSet, CompilerOptionHandler compilerOptionHandler, |
||||
List<RepositoryConfiguration> repositoryConfiguration) { |
||||
this.options = optionSet; |
||||
this.optionHandler = compilerOptionHandler; |
||||
this.repositoryConfiguration = repositoryConfiguration; |
||||
} |
||||
|
||||
protected OptionSet getOptions() { |
||||
return this.options; |
||||
} |
||||
|
||||
@Override |
||||
public GroovyCompilerScope getScope() { |
||||
return GroovyCompilerScope.DEFAULT; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isGuessImports() { |
||||
return !this.options.has(this.optionHandler.getNoGuessImportsOption()); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isGuessDependencies() { |
||||
return !this.options.has(this.optionHandler.getNoGuessDependenciesOption()); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAutoconfigure() { |
||||
return this.optionHandler.getAutoconfigureOption().value(this.options); |
||||
} |
||||
|
||||
@Override |
||||
public String[] getClasspath() { |
||||
OptionSpec<String> classpathOption = this.optionHandler.getClasspathOption(); |
||||
if (this.options.has(classpathOption)) { |
||||
return this.options.valueOf(classpathOption).split(":"); |
||||
} |
||||
return DEFAULT_CLASSPATH; |
||||
} |
||||
|
||||
@Override |
||||
public List<RepositoryConfiguration> getRepositoryConfiguration() { |
||||
return this.repositoryConfiguration; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isQuiet() { |
||||
return false; |
||||
} |
||||
|
||||
} |
||||
@ -1,143 +0,0 @@
@@ -1,143 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.options; |
||||
|
||||
import java.io.File; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import joptsimple.OptionSet; |
||||
|
||||
import org.springframework.boot.cli.util.ResourceUtils; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Extract source file options (anything following '--' in an {@link OptionSet}). |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Greg Turnquist |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SourceOptions { |
||||
|
||||
private final List<String> sources; |
||||
|
||||
private final List<?> args; |
||||
|
||||
/** |
||||
* Create a new {@link SourceOptions} instance. |
||||
* @param options the source option set |
||||
*/ |
||||
public SourceOptions(OptionSet options) { |
||||
this(options, null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link SourceOptions} instance. |
||||
* @param arguments the source arguments |
||||
*/ |
||||
public SourceOptions(List<?> arguments) { |
||||
this(arguments, null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link SourceOptions} instance. If it is an error to pass options that |
||||
* specify non-existent sources, but the default paths are allowed not to exist (the |
||||
* paths are tested before use). If default paths are provided and the option set |
||||
* contains no source file arguments it is not an error even if none of the default |
||||
* paths exist. |
||||
* @param optionSet the source option set |
||||
* @param classLoader an optional classloader used to try and load files that are not |
||||
* found in the local filesystem |
||||
*/ |
||||
public SourceOptions(OptionSet optionSet, ClassLoader classLoader) { |
||||
this(optionSet.nonOptionArguments(), classLoader); |
||||
} |
||||
|
||||
private SourceOptions(List<?> nonOptionArguments, ClassLoader classLoader) { |
||||
List<String> sources = new ArrayList<>(); |
||||
int sourceArgCount = 0; |
||||
for (Object option : nonOptionArguments) { |
||||
if (option instanceof String filename) { |
||||
if ("--".equals(filename)) { |
||||
break; |
||||
} |
||||
List<String> urls = new ArrayList<>(); |
||||
File fileCandidate = new File(filename); |
||||
if (fileCandidate.isFile()) { |
||||
urls.add(fileCandidate.getAbsoluteFile().toURI().toString()); |
||||
} |
||||
else if (!isAbsoluteWindowsFile(fileCandidate)) { |
||||
urls.addAll(ResourceUtils.getUrls(filename, classLoader)); |
||||
} |
||||
for (String url : urls) { |
||||
if (isSource(url)) { |
||||
sources.add(url); |
||||
} |
||||
} |
||||
if (isSource(filename)) { |
||||
if (urls.isEmpty()) { |
||||
throw new IllegalArgumentException("Can't find " + filename); |
||||
} |
||||
else { |
||||
sourceArgCount++; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
this.args = Collections.unmodifiableList(nonOptionArguments.subList(sourceArgCount, nonOptionArguments.size())); |
||||
Assert.isTrue(!sources.isEmpty(), "Please specify at least one file"); |
||||
this.sources = Collections.unmodifiableList(sources); |
||||
} |
||||
|
||||
private boolean isAbsoluteWindowsFile(File file) { |
||||
return isWindows() && file.isAbsolute(); |
||||
} |
||||
|
||||
private boolean isWindows() { |
||||
return File.separatorChar == '\\'; |
||||
} |
||||
|
||||
private boolean isSource(String name) { |
||||
return name.endsWith(".java") || name.endsWith(".groovy"); |
||||
} |
||||
|
||||
public List<?> getArgs() { |
||||
return this.args; |
||||
} |
||||
|
||||
public String[] getArgsArray() { |
||||
return this.args.stream().map(this::asString).toArray(String[]::new); |
||||
} |
||||
|
||||
private String asString(Object arg) { |
||||
return (arg != null) ? String.valueOf(arg) : null; |
||||
} |
||||
|
||||
public List<String> getSources() { |
||||
return this.sources; |
||||
} |
||||
|
||||
public String[] getSourcesArray() { |
||||
return StringUtils.toStringArray(this.sources); |
||||
} |
||||
|
||||
} |
||||
@ -1,159 +0,0 @@
@@ -1,159 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.run; |
||||
|
||||
import java.io.File; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.logging.Level; |
||||
|
||||
import joptsimple.OptionSet; |
||||
import joptsimple.OptionSpec; |
||||
|
||||
import org.springframework.boot.cli.command.Command; |
||||
import org.springframework.boot.cli.command.OptionParsingCommand; |
||||
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
||||
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration; |
||||
import org.springframework.boot.cli.command.options.SourceOptions; |
||||
import org.springframework.boot.cli.command.status.ExitStatus; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerScope; |
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
|
||||
/** |
||||
* {@link Command} to 'run' a groovy script or scripts. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
* @see SpringApplicationRunner |
||||
*/ |
||||
public class RunCommand extends OptionParsingCommand { |
||||
|
||||
public RunCommand() { |
||||
super("run", "Run a spring groovy script", new RunOptionHandler()); |
||||
} |
||||
|
||||
@Override |
||||
public String getUsageHelp() { |
||||
return "[options] <files> [--] [args]"; |
||||
} |
||||
|
||||
public void stop() { |
||||
if (getHandler() != null) { |
||||
((RunOptionHandler) getHandler()).stop(); |
||||
} |
||||
} |
||||
|
||||
private static class RunOptionHandler extends CompilerOptionHandler { |
||||
|
||||
private final Object monitor = new Object(); |
||||
|
||||
private OptionSpec<Void> watchOption; |
||||
|
||||
private OptionSpec<Void> verboseOption; |
||||
|
||||
private OptionSpec<Void> quietOption; |
||||
|
||||
private SpringApplicationRunner runner; |
||||
|
||||
@Override |
||||
protected void doOptions() { |
||||
this.watchOption = option("watch", "Watch the specified file for changes"); |
||||
this.verboseOption = option(Arrays.asList("verbose", "v"), "Verbose logging of dependency resolution"); |
||||
this.quietOption = option(Arrays.asList("quiet", "q"), "Quiet logging"); |
||||
} |
||||
|
||||
void stop() { |
||||
synchronized (this.monitor) { |
||||
if (this.runner != null) { |
||||
this.runner.stop(); |
||||
} |
||||
this.runner = null; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected synchronized ExitStatus run(OptionSet options) throws Exception { |
||||
synchronized (this.monitor) { |
||||
if (this.runner != null) { |
||||
throw new RuntimeException( |
||||
"Already running. Please stop the current application before running another (use the 'stop' command)."); |
||||
} |
||||
|
||||
SourceOptions sourceOptions = new SourceOptions(options); |
||||
|
||||
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory |
||||
.createDefaultRepositoryConfiguration(); |
||||
repositoryConfiguration.add(0, |
||||
new RepositoryConfiguration("local", new File("repository").toURI(), true)); |
||||
|
||||
SpringApplicationRunnerConfiguration configuration = new SpringApplicationRunnerConfigurationAdapter( |
||||
options, this, repositoryConfiguration); |
||||
|
||||
this.runner = new SpringApplicationRunner(configuration, sourceOptions.getSourcesArray(), |
||||
sourceOptions.getArgsArray()); |
||||
this.runner.compileAndRun(); |
||||
|
||||
return ExitStatus.OK; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Simple adapter class to present the {@link OptionSet} as a |
||||
* {@link SpringApplicationRunnerConfiguration}. |
||||
*/ |
||||
private class SpringApplicationRunnerConfigurationAdapter extends OptionSetGroovyCompilerConfiguration |
||||
implements SpringApplicationRunnerConfiguration { |
||||
|
||||
SpringApplicationRunnerConfigurationAdapter(OptionSet options, CompilerOptionHandler optionHandler, |
||||
List<RepositoryConfiguration> repositoryConfiguration) { |
||||
super(options, optionHandler, repositoryConfiguration); |
||||
} |
||||
|
||||
@Override |
||||
public GroovyCompilerScope getScope() { |
||||
return GroovyCompilerScope.DEFAULT; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isWatchForFileChanges() { |
||||
return getOptions().has(RunOptionHandler.this.watchOption); |
||||
} |
||||
|
||||
@Override |
||||
public Level getLogLevel() { |
||||
if (isQuiet()) { |
||||
return Level.OFF; |
||||
} |
||||
if (getOptions().has(RunOptionHandler.this.verboseOption)) { |
||||
return Level.FINEST; |
||||
} |
||||
return Level.INFO; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isQuiet() { |
||||
return getOptions().has(RunOptionHandler.this.quietOption); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,270 +0,0 @@
@@ -1,270 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.run; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.lang.reflect.Method; |
||||
import java.net.MalformedURLException; |
||||
import java.net.URL; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.concurrent.TimeUnit; |
||||
import java.util.logging.Level; |
||||
|
||||
import org.springframework.boot.cli.app.SpringApplicationLauncher; |
||||
import org.springframework.boot.cli.compiler.GroovyCompiler; |
||||
import org.springframework.boot.cli.util.ResourceUtils; |
||||
|
||||
/** |
||||
* Compiles Groovy code running the resulting classes using a {@code SpringApplication}. |
||||
* Takes care of threading and class-loading issues and can optionally monitor sources for |
||||
* changes. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringApplicationRunner { |
||||
|
||||
private static int watcherCounter = 0; |
||||
|
||||
private static int runnerCounter = 0; |
||||
|
||||
private final Object monitor = new Object(); |
||||
|
||||
private final SpringApplicationRunnerConfiguration configuration; |
||||
|
||||
private final String[] sources; |
||||
|
||||
private final String[] args; |
||||
|
||||
private final GroovyCompiler compiler; |
||||
|
||||
private RunThread runThread; |
||||
|
||||
private FileWatchThread fileWatchThread; |
||||
|
||||
/** |
||||
* Create a new {@link SpringApplicationRunner} instance. |
||||
* @param configuration the configuration |
||||
* @param sources the files to compile/watch |
||||
* @param args input arguments |
||||
*/ |
||||
SpringApplicationRunner(SpringApplicationRunnerConfiguration configuration, String[] sources, String... args) { |
||||
this.configuration = configuration; |
||||
this.sources = sources.clone(); |
||||
this.args = args.clone(); |
||||
this.compiler = new GroovyCompiler(configuration); |
||||
int level = configuration.getLogLevel().intValue(); |
||||
if (level <= Level.FINER.intValue()) { |
||||
System.setProperty("org.springframework.boot.cli.compiler.grape.ProgressReporter", "detail"); |
||||
System.setProperty("trace", "true"); |
||||
} |
||||
else if (level <= Level.FINE.intValue()) { |
||||
System.setProperty("debug", "true"); |
||||
} |
||||
else if (level == Level.OFF.intValue()) { |
||||
System.setProperty("spring.main.banner-mode", "OFF"); |
||||
System.setProperty("logging.level.ROOT", "OFF"); |
||||
System.setProperty("org.springframework.boot.cli.compiler.grape.ProgressReporter", "none"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Compile and run the application. |
||||
* @throws Exception on error |
||||
*/ |
||||
public void compileAndRun() throws Exception { |
||||
synchronized (this.monitor) { |
||||
try { |
||||
stop(); |
||||
Class<?>[] compiledSources = compile(); |
||||
monitorForChanges(); |
||||
// Run in new thread to ensure that the context classloader is set up
|
||||
this.runThread = new RunThread(compiledSources); |
||||
this.runThread.start(); |
||||
this.runThread.join(); |
||||
} |
||||
catch (Exception ex) { |
||||
if (this.fileWatchThread == null) { |
||||
throw ex; |
||||
} |
||||
else { |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void stop() { |
||||
synchronized (this.monitor) { |
||||
if (this.runThread != null) { |
||||
this.runThread.shutdown(); |
||||
this.runThread = null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
private Class<?>[] compile() throws IOException { |
||||
Class<?>[] compiledSources = this.compiler.compile(this.sources); |
||||
if (compiledSources.length == 0) { |
||||
throw new RuntimeException("No classes found in '" + Arrays.toString(this.sources) + "'"); |
||||
} |
||||
return compiledSources; |
||||
} |
||||
|
||||
private void monitorForChanges() { |
||||
if (this.fileWatchThread == null && this.configuration.isWatchForFileChanges()) { |
||||
this.fileWatchThread = new FileWatchThread(); |
||||
this.fileWatchThread.start(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Thread used to launch the Spring Application with the correct context classloader. |
||||
*/ |
||||
private class RunThread extends Thread { |
||||
|
||||
private final Object monitor = new Object(); |
||||
|
||||
private final Class<?>[] compiledSources; |
||||
|
||||
private Object applicationContext; |
||||
|
||||
/** |
||||
* Create a new {@link RunThread} instance. |
||||
* @param compiledSources the sources to launch |
||||
*/ |
||||
RunThread(Class<?>... compiledSources) { |
||||
super("runner-" + (runnerCounter++)); |
||||
this.compiledSources = compiledSources; |
||||
if (compiledSources.length != 0) { |
||||
setContextClassLoader(compiledSources[0].getClassLoader()); |
||||
} |
||||
setDaemon(true); |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
synchronized (this.monitor) { |
||||
try { |
||||
this.applicationContext = new SpringApplicationLauncher(getContextClassLoader()) |
||||
.launch(this.compiledSources, SpringApplicationRunner.this.args); |
||||
} |
||||
catch (Exception ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Shutdown the thread, closing any previously opened application context. |
||||
*/ |
||||
void shutdown() { |
||||
synchronized (this.monitor) { |
||||
if (this.applicationContext != null) { |
||||
try { |
||||
Method method = this.applicationContext.getClass().getMethod("close"); |
||||
method.invoke(this.applicationContext); |
||||
} |
||||
catch (NoSuchMethodException ex) { |
||||
// Not an application context that we can close
|
||||
} |
||||
catch (Exception ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
finally { |
||||
this.applicationContext = null; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Thread to watch for file changes and trigger recompile/reload. |
||||
*/ |
||||
private class FileWatchThread extends Thread { |
||||
|
||||
private long previous; |
||||
|
||||
private List<File> sources; |
||||
|
||||
FileWatchThread() { |
||||
super("filewatcher-" + (watcherCounter++)); |
||||
this.previous = 0; |
||||
this.sources = getSourceFiles(); |
||||
for (File file : this.sources) { |
||||
if (file.exists()) { |
||||
long current = file.lastModified(); |
||||
if (current > this.previous) { |
||||
this.previous = current; |
||||
} |
||||
} |
||||
} |
||||
setDaemon(false); |
||||
} |
||||
|
||||
private List<File> getSourceFiles() { |
||||
List<File> sources = new ArrayList<>(); |
||||
for (String source : SpringApplicationRunner.this.sources) { |
||||
List<String> paths = ResourceUtils.getUrls(source, SpringApplicationRunner.this.compiler.getLoader()); |
||||
for (String path : paths) { |
||||
try { |
||||
URL url = new URL(path); |
||||
if ("file".equals(url.getProtocol())) { |
||||
sources.add(new File(url.getFile())); |
||||
} |
||||
} |
||||
catch (MalformedURLException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
} |
||||
return sources; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
while (true) { |
||||
try { |
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1)); |
||||
for (File file : this.sources) { |
||||
if (file.exists()) { |
||||
long current = file.lastModified(); |
||||
if (this.previous < current) { |
||||
this.previous = current; |
||||
compileAndRun(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
catch (InterruptedException ex) { |
||||
Thread.currentThread().interrupt(); |
||||
} |
||||
catch (Exception ex) { |
||||
// Swallow, will be reported by compileAndRun
|
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,44 +0,0 @@
@@ -1,44 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.command.run; |
||||
|
||||
import java.util.logging.Level; |
||||
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
|
||||
/** |
||||
* Configuration for the {@link SpringApplicationRunner}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public interface SpringApplicationRunnerConfiguration extends GroovyCompilerConfiguration { |
||||
|
||||
/** |
||||
* Returns {@code true} if the source file should be monitored for changes and |
||||
* automatically recompiled. |
||||
* @return {@code true} if file watching should be performed, otherwise {@code false} |
||||
*/ |
||||
boolean isWatchForFileChanges(); |
||||
|
||||
/** |
||||
* Returns the logging level to use. |
||||
* @return the logging level |
||||
*/ |
||||
Level getLogLevel(); |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Classes for running CLI applications. |
||||
*/ |
||||
package org.springframework.boot.cli.command.run; |
||||
@ -1,125 +0,0 @@
@@ -1,125 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.AnnotatedNode; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassCodeVisitorSupport; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.ImportNode; |
||||
import org.codehaus.groovy.ast.ModuleNode; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
/** |
||||
* A base class for {@link ASTTransformation AST transformations} that are solely |
||||
* interested in {@link AnnotatedNode AnnotatedNodes}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.1.0 |
||||
*/ |
||||
public abstract class AnnotatedNodeASTTransformation implements ASTTransformation { |
||||
|
||||
private final Set<String> interestingAnnotationNames; |
||||
|
||||
private final boolean removeAnnotations; |
||||
|
||||
private SourceUnit sourceUnit; |
||||
|
||||
protected AnnotatedNodeASTTransformation(Set<String> interestingAnnotationNames, boolean removeAnnotations) { |
||||
this.interestingAnnotationNames = interestingAnnotationNames; |
||||
this.removeAnnotations = removeAnnotations; |
||||
} |
||||
|
||||
@Override |
||||
public void visit(ASTNode[] nodes, SourceUnit source) { |
||||
this.sourceUnit = source; |
||||
List<AnnotationNode> annotationNodes = new ArrayList<>(); |
||||
ClassVisitor classVisitor = new ClassVisitor(source, annotationNodes); |
||||
for (ASTNode node : nodes) { |
||||
if (node instanceof ModuleNode module) { |
||||
visitAnnotatedNode(module.getPackage(), annotationNodes); |
||||
for (ImportNode importNode : module.getImports()) { |
||||
visitAnnotatedNode(importNode, annotationNodes); |
||||
} |
||||
for (ImportNode importNode : module.getStarImports()) { |
||||
visitAnnotatedNode(importNode, annotationNodes); |
||||
} |
||||
module.getStaticImports() |
||||
.forEach((name, importNode) -> visitAnnotatedNode(importNode, annotationNodes)); |
||||
module.getStaticStarImports() |
||||
.forEach((name, importNode) -> visitAnnotatedNode(importNode, annotationNodes)); |
||||
for (ClassNode classNode : module.getClasses()) { |
||||
visitAnnotatedNode(classNode, annotationNodes); |
||||
classNode.visitContents(classVisitor); |
||||
} |
||||
} |
||||
} |
||||
processAnnotationNodes(annotationNodes); |
||||
} |
||||
|
||||
protected SourceUnit getSourceUnit() { |
||||
return this.sourceUnit; |
||||
} |
||||
|
||||
protected abstract void processAnnotationNodes(List<AnnotationNode> annotationNodes); |
||||
|
||||
private void visitAnnotatedNode(AnnotatedNode annotatedNode, List<AnnotationNode> annotatedNodes) { |
||||
if (annotatedNode != null) { |
||||
Iterator<AnnotationNode> annotationNodes = annotatedNode.getAnnotations().iterator(); |
||||
while (annotationNodes.hasNext()) { |
||||
AnnotationNode annotationNode = annotationNodes.next(); |
||||
if (this.interestingAnnotationNames.contains(annotationNode.getClassNode().getName())) { |
||||
annotatedNodes.add(annotationNode); |
||||
if (this.removeAnnotations) { |
||||
annotationNodes.remove(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private class ClassVisitor extends ClassCodeVisitorSupport { |
||||
|
||||
private final SourceUnit source; |
||||
|
||||
private final List<AnnotationNode> annotationNodes; |
||||
|
||||
ClassVisitor(SourceUnit source, List<AnnotationNode> annotationNodes) { |
||||
this.source = source; |
||||
this.annotationNodes = annotationNodes; |
||||
} |
||||
|
||||
@Override |
||||
protected SourceUnit getSourceUnit() { |
||||
return this.source; |
||||
} |
||||
|
||||
@Override |
||||
public void visitAnnotations(AnnotatedNode node) { |
||||
visitAnnotatedNode(node, this.annotationNodes); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,187 +0,0 @@
@@ -1,187 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import org.codehaus.groovy.ast.AnnotatedNode; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.FieldNode; |
||||
import org.codehaus.groovy.ast.MethodNode; |
||||
import org.codehaus.groovy.ast.expr.ArgumentListExpression; |
||||
import org.codehaus.groovy.ast.expr.ClosureExpression; |
||||
import org.codehaus.groovy.ast.expr.ConstantExpression; |
||||
import org.codehaus.groovy.ast.expr.Expression; |
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression; |
||||
import org.codehaus.groovy.ast.stmt.BlockStatement; |
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement; |
||||
import org.codehaus.groovy.ast.stmt.Statement; |
||||
|
||||
import org.springframework.util.PatternMatchUtils; |
||||
|
||||
/** |
||||
* General purpose AST utilities. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Greg Turnquist |
||||
* @since 1.0.0 |
||||
*/ |
||||
public abstract class AstUtils { |
||||
|
||||
/** |
||||
* Determine if a {@link ClassNode} has one or more of the specified annotations on |
||||
* the class or any of its methods. N.B. the type names are not normally fully |
||||
* qualified. |
||||
* @param node the class to examine |
||||
* @param annotations the annotations to look for |
||||
* @return {@code true} if at least one of the annotations is found, otherwise |
||||
* {@code false} |
||||
*/ |
||||
public static boolean hasAtLeastOneAnnotation(ClassNode node, String... annotations) { |
||||
if (hasAtLeastOneAnnotation((AnnotatedNode) node, annotations)) { |
||||
return true; |
||||
} |
||||
for (MethodNode method : node.getMethods()) { |
||||
if (hasAtLeastOneAnnotation(method, annotations)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Determine if an {@link AnnotatedNode} has one or more of the specified annotations. |
||||
* N.B. the annotation type names are not normally fully qualified. |
||||
* @param node the node to examine |
||||
* @param annotations the annotations to look for |
||||
* @return {@code true} if at least one of the annotations is found, otherwise |
||||
* {@code false} |
||||
*/ |
||||
public static boolean hasAtLeastOneAnnotation(AnnotatedNode node, String... annotations) { |
||||
for (AnnotationNode annotationNode : node.getAnnotations()) { |
||||
for (String annotation : annotations) { |
||||
if (PatternMatchUtils.simpleMatch(annotation, annotationNode.getClassNode().getName())) { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Determine if a {@link ClassNode} has one or more fields of the specified types or |
||||
* method returning one or more of the specified types. N.B. the type names are not |
||||
* normally fully qualified. |
||||
* @param node the class to examine |
||||
* @param types the types to look for |
||||
* @return {@code true} if at least one of the types is found, otherwise {@code false} |
||||
*/ |
||||
public static boolean hasAtLeastOneFieldOrMethod(ClassNode node, String... types) { |
||||
Set<String> typesSet = new HashSet<>(Arrays.asList(types)); |
||||
for (FieldNode field : node.getFields()) { |
||||
if (typesSet.contains(field.getType().getName())) { |
||||
return true; |
||||
} |
||||
} |
||||
for (MethodNode method : node.getMethods()) { |
||||
if (typesSet.contains(method.getReturnType().getName())) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Determine if a {@link ClassNode} subclasses any of the specified types N.B. the |
||||
* type names are not normally fully qualified. |
||||
* @param node the class to examine |
||||
* @param types the types that may have been sub-classed |
||||
* @return {@code true} if the class subclasses any of the specified types, otherwise |
||||
* {@code false} |
||||
*/ |
||||
public static boolean subclasses(ClassNode node, String... types) { |
||||
for (String type : types) { |
||||
if (node.getSuperClass().getName().equals(type)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public static boolean hasAtLeastOneInterface(ClassNode classNode, String... types) { |
||||
Set<String> typesSet = new HashSet<>(Arrays.asList(types)); |
||||
for (ClassNode inter : classNode.getInterfaces()) { |
||||
if (typesSet.contains(inter.getName())) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Extract a top-level {@code name} closure from inside this block if there is one, |
||||
* optionally removing it from the block at the same time. |
||||
* @param block a block statement (class definition) |
||||
* @param name the name to look for |
||||
* @param remove whether the extracted closure should be removed |
||||
* @return a beans Closure if one can be found, null otherwise |
||||
*/ |
||||
public static ClosureExpression getClosure(BlockStatement block, String name, boolean remove) { |
||||
for (ExpressionStatement statement : getExpressionStatements(block)) { |
||||
Expression expression = statement.getExpression(); |
||||
if (expression instanceof MethodCallExpression methodCallExpression) { |
||||
ClosureExpression closure = getClosure(name, methodCallExpression); |
||||
if (closure != null) { |
||||
if (remove) { |
||||
block.getStatements().remove(statement); |
||||
} |
||||
return closure; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private static List<ExpressionStatement> getExpressionStatements(BlockStatement block) { |
||||
List<ExpressionStatement> statements = new ArrayList<>(); |
||||
for (Statement statement : block.getStatements()) { |
||||
if (statement instanceof ExpressionStatement expressionStatement) { |
||||
statements.add(expressionStatement); |
||||
} |
||||
} |
||||
return statements; |
||||
} |
||||
|
||||
private static ClosureExpression getClosure(String name, MethodCallExpression expression) { |
||||
Expression method = expression.getMethod(); |
||||
if (method instanceof ConstantExpression constantExpression && name.equals(constantExpression.getValue())) { |
||||
return (ClosureExpression) ((ArgumentListExpression) expression.getArguments()).getExpression(0); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static ClosureExpression getClosure(BlockStatement block, String name) { |
||||
return getClosure(block, name, false); |
||||
} |
||||
|
||||
} |
||||
@ -1,95 +0,0 @@
@@ -1,95 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import groovy.lang.GroovyClassLoader; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.classgen.GeneratorContext; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.CompilePhase; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
/** |
||||
* Strategy that can be used to apply some auto-configuration during the |
||||
* {@link CompilePhase#CONVERSION} Groovy compile phase. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public abstract class CompilerAutoConfiguration { |
||||
|
||||
/** |
||||
* Strategy method used to determine when compiler auto-configuration should be |
||||
* applied. Defaults to always. |
||||
* @param classNode the class node |
||||
* @return {@code true} if the compiler should be auto-configured using this class. If |
||||
* this method returns {@code false} no other strategy methods will be called. |
||||
*/ |
||||
public boolean matches(ClassNode classNode) { |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Apply any dependency customizations. This method will only be called if |
||||
* {@link #matches} returns {@code true}. |
||||
* @param dependencies dependency customizer |
||||
* @throws CompilationFailedException if the dependencies cannot be applied |
||||
*/ |
||||
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { |
||||
} |
||||
|
||||
/** |
||||
* Apply any import customizations. This method will only be called if |
||||
* {@link #matches} returns {@code true}. |
||||
* @param imports import customizer |
||||
* @throws CompilationFailedException if the imports cannot be applied |
||||
*/ |
||||
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { |
||||
} |
||||
|
||||
/** |
||||
* Apply any customizations to the main class. This method will only be called if |
||||
* {@link #matches} returns {@code true}. This method is useful when a groovy file |
||||
* defines more than one class but customization only applies to the first class. |
||||
* @param loader the class loader being used during compilation |
||||
* @param configuration the compiler configuration |
||||
* @param generatorContext the current context |
||||
* @param source the source unit |
||||
* @param classNode the main class
|
||||
* @throws CompilationFailedException if the customizations cannot be applied |
||||
*/ |
||||
public void applyToMainClass(GroovyClassLoader loader, GroovyCompilerConfiguration configuration, |
||||
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode) |
||||
throws CompilationFailedException { |
||||
} |
||||
|
||||
/** |
||||
* Apply any additional configuration. |
||||
* @param loader the class loader being used during compilation |
||||
* @param configuration the compiler configuration |
||||
* @param generatorContext the current context |
||||
* @param source the source unit |
||||
* @param classNode the class
|
||||
* @throws CompilationFailedException if the configuration cannot be applied |
||||
*/ |
||||
public void apply(GroovyClassLoader loader, GroovyCompilerConfiguration configuration, |
||||
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode) |
||||
throws CompilationFailedException { |
||||
} |
||||
|
||||
} |
||||
@ -1,83 +0,0 @@
@@ -1,83 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import groovy.lang.GroovyClassLoader; |
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.ModuleNode; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; |
||||
import org.springframework.core.annotation.Order; |
||||
|
||||
/** |
||||
* {@link ASTTransformation} to apply |
||||
* {@link CompilerAutoConfiguration#applyDependencies(DependencyCustomizer) dependency |
||||
* auto-configuration}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
@Order(DependencyAutoConfigurationTransformation.ORDER) |
||||
public class DependencyAutoConfigurationTransformation implements ASTTransformation { |
||||
|
||||
/** |
||||
* The order of the transformation. |
||||
*/ |
||||
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 100; |
||||
|
||||
private final GroovyClassLoader loader; |
||||
|
||||
private final DependencyResolutionContext dependencyResolutionContext; |
||||
|
||||
private final Iterable<CompilerAutoConfiguration> compilerAutoConfigurations; |
||||
|
||||
public DependencyAutoConfigurationTransformation(GroovyClassLoader loader, |
||||
DependencyResolutionContext dependencyResolutionContext, |
||||
Iterable<CompilerAutoConfiguration> compilerAutoConfigurations) { |
||||
this.loader = loader; |
||||
this.dependencyResolutionContext = dependencyResolutionContext; |
||||
this.compilerAutoConfigurations = compilerAutoConfigurations; |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void visit(ASTNode[] nodes, SourceUnit source) { |
||||
for (ASTNode astNode : nodes) { |
||||
if (astNode instanceof ModuleNode moduleNode) { |
||||
visitModule(moduleNode); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void visitModule(ModuleNode module) { |
||||
DependencyCustomizer dependencies = new DependencyCustomizer(this.loader, module, |
||||
this.dependencyResolutionContext); |
||||
for (ClassNode classNode : module.getClasses()) { |
||||
for (CompilerAutoConfiguration autoConfiguration : this.compilerAutoConfigurations) { |
||||
if (autoConfiguration.matches(classNode)) { |
||||
autoConfiguration.applyDependencies(dependencies); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,262 +0,0 @@
@@ -1,262 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import groovy.lang.Grab; |
||||
import groovy.lang.GroovyClassLoader; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.ModuleNode; |
||||
import org.codehaus.groovy.ast.expr.ConstantExpression; |
||||
|
||||
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver; |
||||
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; |
||||
|
||||
/** |
||||
* Customizer that allows dependencies to be added during compilation. Adding a dependency |
||||
* results in a {@link Grab @Grab} annotation being added to the primary {@link ClassNode |
||||
* class} is the {@link ModuleNode module} that's being customized. |
||||
* <p> |
||||
* This class provides a fluent API for conditionally adding dependencies. For example: |
||||
* {@code dependencies.ifMissing("com.corp.SomeClass").add(module)}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DependencyCustomizer { |
||||
|
||||
private final GroovyClassLoader loader; |
||||
|
||||
private final ClassNode classNode; |
||||
|
||||
private final DependencyResolutionContext dependencyResolutionContext; |
||||
|
||||
/** |
||||
* Create a new {@link DependencyCustomizer} instance. |
||||
* @param loader the current classloader |
||||
* @param moduleNode the current module |
||||
* @param dependencyResolutionContext the context for dependency resolution |
||||
*/ |
||||
public DependencyCustomizer(GroovyClassLoader loader, ModuleNode moduleNode, |
||||
DependencyResolutionContext dependencyResolutionContext) { |
||||
this.loader = loader; |
||||
this.classNode = moduleNode.getClasses().get(0); |
||||
this.dependencyResolutionContext = dependencyResolutionContext; |
||||
} |
||||
|
||||
/** |
||||
* Create a new nested {@link DependencyCustomizer}. |
||||
* @param parent the parent customizer |
||||
*/ |
||||
protected DependencyCustomizer(DependencyCustomizer parent) { |
||||
this.loader = parent.loader; |
||||
this.classNode = parent.classNode; |
||||
this.dependencyResolutionContext = parent.dependencyResolutionContext; |
||||
} |
||||
|
||||
public String getVersion(String artifactId) { |
||||
return getVersion(artifactId, ""); |
||||
} |
||||
|
||||
public String getVersion(String artifactId, String defaultVersion) { |
||||
String version = this.dependencyResolutionContext.getArtifactCoordinatesResolver().getVersion(artifactId); |
||||
if (version == null) { |
||||
version = defaultVersion; |
||||
} |
||||
return version; |
||||
} |
||||
|
||||
/** |
||||
* Create a nested {@link DependencyCustomizer} that only applies if any of the |
||||
* specified class names are not on the class path. |
||||
* @param classNames the class names to test |
||||
* @return a nested {@link DependencyCustomizer} |
||||
*/ |
||||
public DependencyCustomizer ifAnyMissingClasses(String... classNames) { |
||||
return new DependencyCustomizer(this) { |
||||
@Override |
||||
protected boolean canAdd() { |
||||
for (String className : classNames) { |
||||
try { |
||||
Class.forName(className, false, DependencyCustomizer.this.loader); |
||||
} |
||||
catch (Exception ex) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Create a nested {@link DependencyCustomizer} that only applies if all the specified |
||||
* class names are not on the class path. |
||||
* @param classNames the class names to test |
||||
* @return a nested {@link DependencyCustomizer} |
||||
*/ |
||||
public DependencyCustomizer ifAllMissingClasses(String... classNames) { |
||||
return new DependencyCustomizer(this) { |
||||
@Override |
||||
protected boolean canAdd() { |
||||
for (String className : classNames) { |
||||
try { |
||||
Class.forName(className, false, DependencyCustomizer.this.loader); |
||||
return false; |
||||
} |
||||
catch (Exception ex) { |
||||
// swallow exception and continue
|
||||
} |
||||
} |
||||
return DependencyCustomizer.this.canAdd(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Create a nested {@link DependencyCustomizer} that only applies if the specified |
||||
* paths are on the class path. |
||||
* @param paths the paths to test |
||||
* @return a nested {@link DependencyCustomizer} |
||||
*/ |
||||
public DependencyCustomizer ifAllResourcesPresent(String... paths) { |
||||
return new DependencyCustomizer(this) { |
||||
@Override |
||||
protected boolean canAdd() { |
||||
for (String path : paths) { |
||||
try { |
||||
if (DependencyCustomizer.this.loader.getResource(path) == null) { |
||||
return false; |
||||
} |
||||
} |
||||
catch (Exception ex) { |
||||
// swallow exception and continue
|
||||
} |
||||
} |
||||
return DependencyCustomizer.this.canAdd(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Create a nested {@link DependencyCustomizer} that only applies at least one of the |
||||
* specified paths is on the class path. |
||||
* @param paths the paths to test |
||||
* @return a nested {@link DependencyCustomizer} |
||||
*/ |
||||
public DependencyCustomizer ifAnyResourcesPresent(String... paths) { |
||||
return new DependencyCustomizer(this) { |
||||
@Override |
||||
protected boolean canAdd() { |
||||
for (String path : paths) { |
||||
try { |
||||
return DependencyCustomizer.this.loader.getResource(path) != null; |
||||
} |
||||
catch (Exception ex) { |
||||
// swallow exception and continue
|
||||
} |
||||
} |
||||
return DependencyCustomizer.this.canAdd(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Add dependencies and all of their dependencies. The group ID and version of the |
||||
* dependencies are resolved from the modules using the customizer's |
||||
* {@link ArtifactCoordinatesResolver}. |
||||
* @param modules the module IDs |
||||
* @return this {@link DependencyCustomizer} for continued use |
||||
*/ |
||||
public DependencyCustomizer add(String... modules) { |
||||
for (String module : modules) { |
||||
add(module, null, null, true); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Add a single dependency and, optionally, all of its dependencies. The group ID and |
||||
* version of the dependency are resolved from the module using the customizer's |
||||
* {@link ArtifactCoordinatesResolver}. |
||||
* @param module the module ID |
||||
* @param transitive {@code true} if the transitive dependencies should also be added, |
||||
* otherwise {@code false} |
||||
* @return this {@link DependencyCustomizer} for continued use |
||||
*/ |
||||
public DependencyCustomizer add(String module, boolean transitive) { |
||||
return add(module, null, null, transitive); |
||||
} |
||||
|
||||
/** |
||||
* Add a single dependency with the specified classifier and type and, optionally, all |
||||
* of its dependencies. The group ID and version of the dependency are resolved from |
||||
* the module by using the customizer's {@link ArtifactCoordinatesResolver}. |
||||
* @param module the module ID |
||||
* @param classifier the classifier, may be {@code null} |
||||
* @param type the type, may be {@code null} |
||||
* @param transitive {@code true} if the transitive dependencies should also be added, |
||||
* otherwise {@code false} |
||||
* @return this {@link DependencyCustomizer} for continued use |
||||
*/ |
||||
public DependencyCustomizer add(String module, String classifier, String type, boolean transitive) { |
||||
if (canAdd()) { |
||||
ArtifactCoordinatesResolver artifactCoordinatesResolver = this.dependencyResolutionContext |
||||
.getArtifactCoordinatesResolver(); |
||||
this.classNode.addAnnotation(createGrabAnnotation(artifactCoordinatesResolver.getGroupId(module), |
||||
artifactCoordinatesResolver.getArtifactId(module), artifactCoordinatesResolver.getVersion(module), |
||||
classifier, type, transitive)); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
private AnnotationNode createGrabAnnotation(String group, String module, String version, String classifier, |
||||
String type, boolean transitive) { |
||||
AnnotationNode annotationNode = new AnnotationNode(new ClassNode(Grab.class)); |
||||
annotationNode.addMember("group", new ConstantExpression(group)); |
||||
annotationNode.addMember("module", new ConstantExpression(module)); |
||||
annotationNode.addMember("version", new ConstantExpression(version)); |
||||
if (classifier != null) { |
||||
annotationNode.addMember("classifier", new ConstantExpression(classifier)); |
||||
} |
||||
if (type != null) { |
||||
annotationNode.addMember("type", new ConstantExpression(type)); |
||||
} |
||||
annotationNode.addMember("transitive", new ConstantExpression(transitive)); |
||||
annotationNode.addMember("initClass", new ConstantExpression(false)); |
||||
return annotationNode; |
||||
} |
||||
|
||||
/** |
||||
* Strategy called to test if dependencies can be added. Subclasses override as |
||||
* required. Returns {@code true} by default. |
||||
* @return {@code true} if dependencies can be added, otherwise {@code false} |
||||
*/ |
||||
protected boolean canAdd() { |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Returns the {@link DependencyResolutionContext}. |
||||
* @return the dependency resolution context |
||||
*/ |
||||
public DependencyResolutionContext getDependencyResolutionContext() { |
||||
return this.dependencyResolutionContext; |
||||
} |
||||
|
||||
} |
||||
@ -1,241 +0,0 @@
@@ -1,241 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.net.MalformedURLException; |
||||
import java.net.URI; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import groovy.grape.Grape; |
||||
import org.apache.maven.model.Dependency; |
||||
import org.apache.maven.model.Model; |
||||
import org.apache.maven.model.Parent; |
||||
import org.apache.maven.model.Repository; |
||||
import org.apache.maven.model.building.DefaultModelBuilder; |
||||
import org.apache.maven.model.building.DefaultModelBuilderFactory; |
||||
import org.apache.maven.model.building.DefaultModelBuildingRequest; |
||||
import org.apache.maven.model.resolution.InvalidRepositoryException; |
||||
import org.apache.maven.model.resolution.ModelResolver; |
||||
import org.apache.maven.model.resolution.UnresolvableModelException; |
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.expr.ConstantExpression; |
||||
import org.codehaus.groovy.ast.expr.Expression; |
||||
import org.codehaus.groovy.ast.expr.ListExpression; |
||||
import org.codehaus.groovy.control.messages.Message; |
||||
import org.codehaus.groovy.control.messages.SyntaxErrorMessage; |
||||
import org.codehaus.groovy.syntax.SyntaxException; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
import org.springframework.boot.cli.compiler.dependencies.MavenModelDependencyManagement; |
||||
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; |
||||
import org.springframework.boot.groovy.DependencyManagementBom; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.core.annotation.Order; |
||||
|
||||
/** |
||||
* {@link ASTTransformation} for processing |
||||
* {@link DependencyManagementBom @DependencyManagementBom} annotations. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.3.0 |
||||
*/ |
||||
@Order(DependencyManagementBomTransformation.ORDER) |
||||
@SuppressWarnings("deprecation") |
||||
public class DependencyManagementBomTransformation extends AnnotatedNodeASTTransformation { |
||||
|
||||
/** |
||||
* The order of the transformation. |
||||
*/ |
||||
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 100; |
||||
|
||||
private static final Set<String> DEPENDENCY_MANAGEMENT_BOM_ANNOTATION_NAMES = Collections |
||||
.unmodifiableSet(new HashSet<>(Arrays.asList(DependencyManagementBom.class.getName(), |
||||
DependencyManagementBom.class.getSimpleName()))); |
||||
|
||||
private final DependencyResolutionContext resolutionContext; |
||||
|
||||
public DependencyManagementBomTransformation(DependencyResolutionContext resolutionContext) { |
||||
super(DEPENDENCY_MANAGEMENT_BOM_ANNOTATION_NAMES, true); |
||||
this.resolutionContext = resolutionContext; |
||||
} |
||||
|
||||
@Override |
||||
protected void processAnnotationNodes(List<AnnotationNode> annotationNodes) { |
||||
if (!annotationNodes.isEmpty()) { |
||||
if (annotationNodes.size() > 1) { |
||||
for (AnnotationNode annotationNode : annotationNodes) { |
||||
handleDuplicateDependencyManagementBomAnnotation(annotationNode); |
||||
} |
||||
} |
||||
else { |
||||
processDependencyManagementBomAnnotation(annotationNodes.get(0)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void processDependencyManagementBomAnnotation(AnnotationNode annotationNode) { |
||||
Expression valueExpression = annotationNode.getMember("value"); |
||||
List<Map<String, String>> bomDependencies = createDependencyMaps(valueExpression); |
||||
updateDependencyResolutionContext(bomDependencies); |
||||
} |
||||
|
||||
private List<Map<String, String>> createDependencyMaps(Expression valueExpression) { |
||||
Map<String, String> dependency = null; |
||||
List<ConstantExpression> constantExpressions = getConstantExpressions(valueExpression); |
||||
List<Map<String, String>> dependencies = new ArrayList<>(constantExpressions.size()); |
||||
for (ConstantExpression expression : constantExpressions) { |
||||
Object value = expression.getValue(); |
||||
if (value instanceof String string) { |
||||
String[] components = string.split(":"); |
||||
if (components.length == 3) { |
||||
dependency = new HashMap<>(); |
||||
dependency.put("group", components[0]); |
||||
dependency.put("module", components[1]); |
||||
dependency.put("version", components[2]); |
||||
dependency.put("type", "pom"); |
||||
dependencies.add(dependency); |
||||
} |
||||
else { |
||||
handleMalformedDependency(expression); |
||||
} |
||||
} |
||||
} |
||||
return dependencies; |
||||
} |
||||
|
||||
private List<ConstantExpression> getConstantExpressions(Expression valueExpression) { |
||||
if (valueExpression instanceof ListExpression listExpression) { |
||||
return getConstantExpressions(listExpression); |
||||
} |
||||
if (valueExpression instanceof ConstantExpression constantExpression |
||||
&& constantExpression.getValue() instanceof String) { |
||||
return Arrays.asList(constantExpression); |
||||
} |
||||
reportError("@DependencyManagementBom requires an inline constant that is a string or a string array", |
||||
valueExpression); |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
private List<ConstantExpression> getConstantExpressions(ListExpression valueExpression) { |
||||
List<ConstantExpression> expressions = new ArrayList<>(); |
||||
for (Expression expression : valueExpression.getExpressions()) { |
||||
if (expression instanceof ConstantExpression constantExpression |
||||
&& constantExpression.getValue() instanceof String) { |
||||
expressions.add(constantExpression); |
||||
} |
||||
else { |
||||
reportError("Each entry in the array must be an inline string constant", expression); |
||||
} |
||||
} |
||||
return expressions; |
||||
} |
||||
|
||||
private void handleMalformedDependency(Expression expression) { |
||||
Message message = createSyntaxErrorMessage( |
||||
String.format("The string must be of the form \"group:module:version\"%n"), expression); |
||||
getSourceUnit().getErrorCollector().addErrorAndContinue(message); |
||||
} |
||||
|
||||
private void updateDependencyResolutionContext(List<Map<String, String>> bomDependencies) { |
||||
URI[] uris = Grape.getInstance().resolve(null, bomDependencies.toArray(new Map<?, ?>[0])); |
||||
DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance(); |
||||
for (URI uri : uris) { |
||||
try { |
||||
DefaultModelBuildingRequest request = new DefaultModelBuildingRequest(); |
||||
request.setModelResolver(new GrapeModelResolver()); |
||||
request.setModelSource(new org.apache.maven.model.building.UrlModelSource(uri.toURL())); |
||||
request.setSystemProperties(System.getProperties()); |
||||
Model model = modelBuilder.build(request).getEffectiveModel(); |
||||
this.resolutionContext.addDependencyManagement(new MavenModelDependencyManagement(model)); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("Failed to build model for '" + uri + "'. Is it a valid Maven bom?", |
||||
ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void handleDuplicateDependencyManagementBomAnnotation(AnnotationNode annotationNode) { |
||||
Message message = createSyntaxErrorMessage( |
||||
"Duplicate @DependencyManagementBom annotation. It must be declared at most once.", annotationNode); |
||||
getSourceUnit().getErrorCollector().addErrorAndContinue(message); |
||||
} |
||||
|
||||
private void reportError(String message, ASTNode node) { |
||||
getSourceUnit().getErrorCollector().addErrorAndContinue(createSyntaxErrorMessage(message, node)); |
||||
} |
||||
|
||||
private Message createSyntaxErrorMessage(String message, ASTNode node) { |
||||
return new SyntaxErrorMessage(new SyntaxException(message, node.getLineNumber(), node.getColumnNumber(), |
||||
node.getLastLineNumber(), node.getLastColumnNumber()), getSourceUnit()); |
||||
} |
||||
|
||||
private static class GrapeModelResolver implements ModelResolver { |
||||
|
||||
@Override |
||||
public org.apache.maven.model.building.ModelSource resolveModel(Parent parent) |
||||
throws UnresolvableModelException { |
||||
return resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); |
||||
} |
||||
|
||||
@Override |
||||
public org.apache.maven.model.building.ModelSource resolveModel(Dependency dependency) |
||||
throws UnresolvableModelException { |
||||
return resolveModel(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()); |
||||
} |
||||
|
||||
@Override |
||||
public org.apache.maven.model.building.ModelSource resolveModel(String groupId, String artifactId, |
||||
String version) throws UnresolvableModelException { |
||||
Map<String, String> dependency = new HashMap<>(); |
||||
dependency.put("group", groupId); |
||||
dependency.put("module", artifactId); |
||||
dependency.put("version", version); |
||||
dependency.put("type", "pom"); |
||||
try { |
||||
return new org.apache.maven.model.building.UrlModelSource( |
||||
Grape.getInstance().resolve(null, dependency)[0].toURL()); |
||||
} |
||||
catch (MalformedURLException ex) { |
||||
throw new UnresolvableModelException(ex.getMessage(), groupId, artifactId, version); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void addRepository(Repository repository) throws InvalidRepositoryException { |
||||
} |
||||
|
||||
@Override |
||||
public void addRepository(Repository repository, boolean replace) throws InvalidRepositoryException { |
||||
} |
||||
|
||||
@Override |
||||
public ModelResolver newCopy() { |
||||
return this; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,245 +0,0 @@
@@ -1,245 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.MalformedURLException; |
||||
import java.net.URL; |
||||
import java.net.URLClassLoader; |
||||
import java.util.ArrayList; |
||||
import java.util.Enumeration; |
||||
import java.util.HashMap; |
||||
import java.util.HashSet; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import groovy.lang.GroovyClassLoader; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.CompilationUnit; |
||||
import org.codehaus.groovy.control.CompilerConfiguration; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.FileCopyUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Extension of the {@link GroovyClassLoader} with support for obtaining '.class' files as |
||||
* resources. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class ExtendedGroovyClassLoader extends GroovyClassLoader { |
||||
|
||||
private static final String SHARED_PACKAGE = "org.springframework.boot.groovy"; |
||||
|
||||
private static final URL[] NO_URLS = new URL[] {}; |
||||
|
||||
private final Map<String, byte[]> classResources = new HashMap<>(); |
||||
|
||||
private final GroovyCompilerScope scope; |
||||
|
||||
private final CompilerConfiguration configuration; |
||||
|
||||
public ExtendedGroovyClassLoader(GroovyCompilerScope scope) { |
||||
this(scope, createParentClassLoader(scope), new CompilerConfiguration()); |
||||
} |
||||
|
||||
private static ClassLoader createParentClassLoader(GroovyCompilerScope scope) { |
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
||||
if (scope == GroovyCompilerScope.DEFAULT) { |
||||
classLoader = new DefaultScopeParentClassLoader(classLoader); |
||||
} |
||||
return classLoader; |
||||
} |
||||
|
||||
private ExtendedGroovyClassLoader(GroovyCompilerScope scope, ClassLoader parent, |
||||
CompilerConfiguration configuration) { |
||||
super(parent, configuration); |
||||
this.configuration = configuration; |
||||
this.scope = scope; |
||||
} |
||||
|
||||
@Override |
||||
protected Class<?> findClass(String name) throws ClassNotFoundException { |
||||
try { |
||||
return super.findClass(name); |
||||
} |
||||
catch (ClassNotFoundException ex) { |
||||
if (this.scope == GroovyCompilerScope.DEFAULT && name.startsWith(SHARED_PACKAGE)) { |
||||
Class<?> sharedClass = findSharedClass(name); |
||||
if (sharedClass != null) { |
||||
return sharedClass; |
||||
} |
||||
} |
||||
throw ex; |
||||
} |
||||
} |
||||
|
||||
private Class<?> findSharedClass(String name) { |
||||
try { |
||||
String path = name.replace('.', '/').concat(".class"); |
||||
try (InputStream inputStream = getParent().getResourceAsStream(path)) { |
||||
if (inputStream != null) { |
||||
return defineClass(name, FileCopyUtils.copyToByteArray(inputStream)); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
catch (Exception ex) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getResourceAsStream(String name) { |
||||
InputStream resourceStream = super.getResourceAsStream(name); |
||||
if (resourceStream == null) { |
||||
byte[] bytes = this.classResources.get(name); |
||||
resourceStream = (bytes != null) ? new ByteArrayInputStream(bytes) : null; |
||||
} |
||||
return resourceStream; |
||||
} |
||||
|
||||
@Override |
||||
public ClassCollector createCollector(CompilationUnit unit, SourceUnit su) { |
||||
return new ExtendedClassCollector(getInnerLoader(), unit, su); |
||||
} |
||||
|
||||
private InnerLoader getInnerLoader() { |
||||
return new InnerLoader(ExtendedGroovyClassLoader.this) { |
||||
|
||||
// Don't return URLs from the inner loader so that Tomcat only
|
||||
// searches the parent. Fixes 'TLD skipped' issues
|
||||
@Override |
||||
public URL[] getURLs() { |
||||
return NO_URLS; |
||||
} |
||||
|
||||
}; |
||||
} |
||||
|
||||
public CompilerConfiguration getConfiguration() { |
||||
return this.configuration; |
||||
} |
||||
|
||||
/** |
||||
* Inner collector class used to track as classes are added. |
||||
*/ |
||||
protected class ExtendedClassCollector extends ClassCollector { |
||||
|
||||
protected ExtendedClassCollector(InnerLoader loader, CompilationUnit unit, SourceUnit su) { |
||||
super(loader, unit, su); |
||||
} |
||||
|
||||
@Override |
||||
protected Class<?> createClass(byte[] code, ClassNode classNode) { |
||||
Class<?> createdClass = super.createClass(code, classNode); |
||||
ExtendedGroovyClassLoader.this.classResources.put(classNode.getName().replace('.', '/') + ".class", code); |
||||
return createdClass; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* ClassLoader used for a parent that filters so that only classes from groovy-all.jar |
||||
* are exposed. |
||||
*/ |
||||
private static class DefaultScopeParentClassLoader extends ClassLoader { |
||||
|
||||
private static final String[] GROOVY_JARS_PREFIXES = { "groovy", "antlr", "asm" }; |
||||
|
||||
private final URLClassLoader groovyOnlyClassLoader; |
||||
|
||||
DefaultScopeParentClassLoader(ClassLoader parent) { |
||||
super(parent); |
||||
this.groovyOnlyClassLoader = new URLClassLoader(getGroovyJars(parent), |
||||
getClass().getClassLoader().getParent()); |
||||
} |
||||
|
||||
private URL[] getGroovyJars(ClassLoader parent) { |
||||
Set<URL> urls = new HashSet<>(); |
||||
findGroovyJarsDirectly(parent, urls); |
||||
if (urls.isEmpty()) { |
||||
findGroovyJarsFromClassPath(urls); |
||||
} |
||||
Assert.state(!urls.isEmpty(), "Unable to find groovy JAR"); |
||||
return new ArrayList<>(urls).toArray(new URL[0]); |
||||
} |
||||
|
||||
private void findGroovyJarsDirectly(ClassLoader classLoader, Set<URL> urls) { |
||||
while (classLoader != null) { |
||||
if (classLoader instanceof URLClassLoader urlClassLoader) { |
||||
for (URL url : urlClassLoader.getURLs()) { |
||||
if (isGroovyJar(url.toString())) { |
||||
urls.add(url); |
||||
} |
||||
} |
||||
} |
||||
classLoader = classLoader.getParent(); |
||||
} |
||||
} |
||||
|
||||
private void findGroovyJarsFromClassPath(Set<URL> urls) { |
||||
String classpath = System.getProperty("java.class.path"); |
||||
String[] entries = classpath.split(System.getProperty("path.separator")); |
||||
for (String entry : entries) { |
||||
if (isGroovyJar(entry)) { |
||||
File file = new File(entry); |
||||
if (file.canRead()) { |
||||
try { |
||||
urls.add(file.toURI().toURL()); |
||||
} |
||||
catch (MalformedURLException ex) { |
||||
// Swallow and continue
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private boolean isGroovyJar(String entry) { |
||||
entry = StringUtils.cleanPath(entry); |
||||
for (String jarPrefix : GROOVY_JARS_PREFIXES) { |
||||
if (entry.contains("/" + jarPrefix + "-")) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public Enumeration<URL> getResources(String name) throws IOException { |
||||
return this.groovyOnlyClassLoader.getResources(name); |
||||
} |
||||
|
||||
@Override |
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { |
||||
if (!name.startsWith("java.")) { |
||||
Class.forName(name, false, this.groovyOnlyClassLoader); |
||||
} |
||||
return super.loadClass(name, resolve); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,131 +0,0 @@
@@ -1,131 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.AnnotatedNode; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassHelper; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.ModuleNode; |
||||
import org.codehaus.groovy.ast.PackageNode; |
||||
import org.codehaus.groovy.ast.expr.ConstantExpression; |
||||
import org.codehaus.groovy.ast.expr.Expression; |
||||
import org.codehaus.groovy.ast.expr.ListExpression; |
||||
import org.codehaus.groovy.control.CompilePhase; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.transform.GroovyASTTransformation; |
||||
|
||||
import org.springframework.boot.groovy.DependencyManagementBom; |
||||
import org.springframework.core.Ordered; |
||||
|
||||
/** |
||||
* A base class that lets plugin authors easily add additional BOMs to all apps. All the |
||||
* dependencies in the BOM (and its transitives) will be added to the dependency |
||||
* management lookup, so an app can use just the artifact id (e.g. "spring-jdbc") in a |
||||
* {@code @Grab}. To install, implement the missing methods and list the class in |
||||
* {@code META-INF/services/org.springframework.boot.cli.compiler.SpringBootAstTransformation} |
||||
* . The {@link #getOrder()} value needs to be before |
||||
* {@link DependencyManagementBomTransformation#ORDER}. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.3.0 |
||||
*/ |
||||
@GroovyASTTransformation(phase = CompilePhase.CONVERSION) |
||||
public abstract class GenericBomAstTransformation implements SpringBootAstTransformation, Ordered { |
||||
|
||||
private static final ClassNode BOM = ClassHelper.make(DependencyManagementBom.class); |
||||
|
||||
@Override |
||||
public void visit(ASTNode[] nodes, SourceUnit source) { |
||||
for (ASTNode astNode : nodes) { |
||||
if (astNode instanceof ModuleNode moduleNode) { |
||||
visitModule(moduleNode, getBomModule()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* The bom to be added to dependency management in compact form: |
||||
* {@code "<groupId>:<artifactId>:<version>"} (like in a {@code @Grab}). |
||||
* @return the maven co-ordinates of the BOM to add |
||||
*/ |
||||
protected abstract String getBomModule(); |
||||
|
||||
private void visitModule(ModuleNode node, String module) { |
||||
addDependencyManagementBom(node, module); |
||||
} |
||||
|
||||
private void addDependencyManagementBom(ModuleNode node, String module) { |
||||
AnnotatedNode annotated = getAnnotatedNode(node); |
||||
if (annotated != null) { |
||||
AnnotationNode bom = getAnnotation(annotated); |
||||
List<Expression> expressions = new ArrayList<>(getConstantExpressions(bom.getMember("value"))); |
||||
expressions.add(new ConstantExpression(module)); |
||||
bom.setMember("value", new ListExpression(expressions)); |
||||
} |
||||
} |
||||
|
||||
private AnnotationNode getAnnotation(AnnotatedNode annotated) { |
||||
List<AnnotationNode> annotations = annotated.getAnnotations(BOM); |
||||
if (!annotations.isEmpty()) { |
||||
return annotations.get(0); |
||||
} |
||||
AnnotationNode annotation = new AnnotationNode(BOM); |
||||
annotated.addAnnotation(annotation); |
||||
return annotation; |
||||
} |
||||
|
||||
private AnnotatedNode getAnnotatedNode(ModuleNode node) { |
||||
PackageNode packageNode = node.getPackage(); |
||||
if (packageNode != null && !packageNode.getAnnotations(BOM).isEmpty()) { |
||||
return packageNode; |
||||
} |
||||
if (!node.getClasses().isEmpty()) { |
||||
return node.getClasses().get(0); |
||||
} |
||||
return packageNode; |
||||
} |
||||
|
||||
private List<ConstantExpression> getConstantExpressions(Expression valueExpression) { |
||||
if (valueExpression instanceof ListExpression listExpression) { |
||||
return getConstantExpressions(listExpression); |
||||
} |
||||
if (valueExpression instanceof ConstantExpression constantExpression |
||||
&& constantExpression.getValue() instanceof String) { |
||||
return Arrays.asList(constantExpression); |
||||
} |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
private List<ConstantExpression> getConstantExpressions(ListExpression valueExpression) { |
||||
List<ConstantExpression> expressions = new ArrayList<>(); |
||||
for (Expression expression : valueExpression.getExpressions()) { |
||||
if (expression instanceof ConstantExpression constantExpression |
||||
&& constantExpression.getValue() instanceof String) { |
||||
expressions.add(constantExpression); |
||||
} |
||||
} |
||||
return expressions; |
||||
} |
||||
|
||||
} |
||||
@ -1,118 +0,0 @@
@@ -1,118 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.lang.reflect.Modifier; |
||||
import java.util.ArrayList; |
||||
|
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.ClassCodeVisitorSupport; |
||||
import org.codehaus.groovy.ast.ClassHelper; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.ModuleNode; |
||||
import org.codehaus.groovy.ast.PropertyNode; |
||||
import org.codehaus.groovy.ast.expr.ClosureExpression; |
||||
import org.codehaus.groovy.ast.stmt.BlockStatement; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
import org.springframework.core.annotation.Order; |
||||
|
||||
/** |
||||
* {@link ASTTransformation} to resolve beans declarations inside application source |
||||
* files. Users only need to define a <code>beans{}</code> DSL element, and this |
||||
* transformation will remove it and make it accessible to the Spring application via an |
||||
* interface. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
@Order(GroovyBeansTransformation.ORDER) |
||||
public class GroovyBeansTransformation implements ASTTransformation { |
||||
|
||||
/** |
||||
* The order of the transformation. |
||||
*/ |
||||
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 200; |
||||
|
||||
@Override |
||||
public void visit(ASTNode[] nodes, SourceUnit source) { |
||||
for (ASTNode node : nodes) { |
||||
if (node instanceof ModuleNode module) { |
||||
for (ClassNode classNode : new ArrayList<>(module.getClasses())) { |
||||
if (classNode.isScript()) { |
||||
classNode.visitContents(new ClassVisitor(source, classNode)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private class ClassVisitor extends ClassCodeVisitorSupport { |
||||
|
||||
private static final String SOURCE_INTERFACE = "org.springframework.boot.BeanDefinitionLoader.GroovyBeanDefinitionSource"; |
||||
|
||||
private static final String BEANS = "beans"; |
||||
|
||||
private final SourceUnit source; |
||||
|
||||
private final ClassNode classNode; |
||||
|
||||
private boolean xformed = false; |
||||
|
||||
ClassVisitor(SourceUnit source, ClassNode classNode) { |
||||
this.source = source; |
||||
this.classNode = classNode; |
||||
} |
||||
|
||||
@Override |
||||
protected SourceUnit getSourceUnit() { |
||||
return this.source; |
||||
} |
||||
|
||||
@Override |
||||
public void visitBlockStatement(BlockStatement block) { |
||||
if (block.isEmpty() || this.xformed) { |
||||
return; |
||||
} |
||||
ClosureExpression closure = beans(block); |
||||
if (closure != null) { |
||||
// Add a marker interface to the current script
|
||||
this.classNode.addInterface(ClassHelper.make(SOURCE_INTERFACE)); |
||||
// Implement the interface by adding a public read-only property with the
|
||||
// same name as the method in the interface (getBeans). Make it return the
|
||||
// closure.
|
||||
this.classNode.addProperty(new PropertyNode(BEANS, Modifier.PUBLIC | Modifier.FINAL, |
||||
ClassHelper.CLOSURE_TYPE.getPlainNodeReference(), this.classNode, closure, null, null)); |
||||
// Only do this once per class
|
||||
this.xformed = true; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Extract a top-level <code>beans{}</code> closure from inside this block if |
||||
* there is one. Removes it from the block at the same time. |
||||
* @param block a block statement (class definition) |
||||
* @return a beans Closure if one can be found, null otherwise |
||||
*/ |
||||
private ClosureExpression beans(BlockStatement block) { |
||||
return AstUtils.getClosure(block, BEANS, true); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,318 +0,0 @@
@@ -1,318 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.io.IOException; |
||||
import java.lang.reflect.Field; |
||||
import java.net.URL; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.Deque; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
import java.util.ServiceLoader; |
||||
|
||||
import groovy.grape.GrapeEngine; |
||||
import groovy.lang.GroovyClassLoader; |
||||
import groovy.lang.GroovyClassLoader.ClassCollector; |
||||
import groovy.lang.GroovyCodeSource; |
||||
import org.codehaus.groovy.ast.ASTNode; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.classgen.GeneratorContext; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.CompilationUnit; |
||||
import org.codehaus.groovy.control.CompilePhase; |
||||
import org.codehaus.groovy.control.CompilerConfiguration; |
||||
import org.codehaus.groovy.control.Phases; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.control.customizers.CompilationCustomizer; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
import org.codehaus.groovy.transform.ASTTransformationVisitor; |
||||
|
||||
import org.springframework.boot.cli.compiler.dependencies.SpringBootDependenciesDependencyManagement; |
||||
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; |
||||
import org.springframework.boot.cli.compiler.grape.GrapeEngineInstaller; |
||||
import org.springframework.boot.cli.compiler.grape.MavenResolverGrapeEngineFactory; |
||||
import org.springframework.boot.cli.util.ResourceUtils; |
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* Compiler for Groovy sources. Primarily a simple Facade for |
||||
* {@link GroovyClassLoader#parseClass(GroovyCodeSource)} with the following additional |
||||
* features: |
||||
* <ul> |
||||
* <li>{@link CompilerAutoConfiguration} strategies will be read from |
||||
* {@code META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration} |
||||
* (per the standard java {@link ServiceLoader} contract) and applied during compilation |
||||
* </li> |
||||
* |
||||
* <li>Multiple classes can be returned if the Groovy source defines more than one Class |
||||
* </li> |
||||
* |
||||
* <li>Generated class files can also be loaded using |
||||
* {@link ClassLoader#getResource(String)}</li> |
||||
* </ul> |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Dave Syer |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class GroovyCompiler { |
||||
|
||||
private final GroovyCompilerConfiguration configuration; |
||||
|
||||
private final ExtendedGroovyClassLoader loader; |
||||
|
||||
private final Iterable<CompilerAutoConfiguration> compilerAutoConfigurations; |
||||
|
||||
private final List<ASTTransformation> transformations; |
||||
|
||||
/** |
||||
* Create a new {@link GroovyCompiler} instance. |
||||
* @param configuration the compiler configuration |
||||
*/ |
||||
public GroovyCompiler(GroovyCompilerConfiguration configuration) { |
||||
|
||||
this.configuration = configuration; |
||||
this.loader = createLoader(configuration); |
||||
|
||||
DependencyResolutionContext resolutionContext = new DependencyResolutionContext(); |
||||
resolutionContext.addDependencyManagement(new SpringBootDependenciesDependencyManagement()); |
||||
|
||||
GrapeEngine grapeEngine = MavenResolverGrapeEngineFactory.create(this.loader, |
||||
configuration.getRepositoryConfiguration(), resolutionContext, configuration.isQuiet()); |
||||
|
||||
GrapeEngineInstaller.install(grapeEngine); |
||||
|
||||
this.loader.getConfiguration().addCompilationCustomizers(new CompilerAutoConfigureCustomizer()); |
||||
if (configuration.isAutoconfigure()) { |
||||
this.compilerAutoConfigurations = ServiceLoader.load(CompilerAutoConfiguration.class); |
||||
} |
||||
else { |
||||
this.compilerAutoConfigurations = Collections.emptySet(); |
||||
} |
||||
|
||||
this.transformations = new ArrayList<>(); |
||||
this.transformations.add(new DependencyManagementBomTransformation(resolutionContext)); |
||||
this.transformations.add(new DependencyAutoConfigurationTransformation(this.loader, resolutionContext, |
||||
this.compilerAutoConfigurations)); |
||||
this.transformations.add(new GroovyBeansTransformation()); |
||||
if (this.configuration.isGuessDependencies()) { |
||||
this.transformations.add(new ResolveDependencyCoordinatesTransformation(resolutionContext)); |
||||
} |
||||
for (ASTTransformation transformation : ServiceLoader.load(SpringBootAstTransformation.class)) { |
||||
this.transformations.add(transformation); |
||||
} |
||||
this.transformations.sort(AnnotationAwareOrderComparator.INSTANCE); |
||||
} |
||||
|
||||
/** |
||||
* Return a mutable list of the {@link ASTTransformation}s to be applied during |
||||
* {@link #compile(String...)}. |
||||
* @return the AST transformations to apply |
||||
*/ |
||||
public List<ASTTransformation> getAstTransformations() { |
||||
return this.transformations; |
||||
} |
||||
|
||||
public ExtendedGroovyClassLoader getLoader() { |
||||
return this.loader; |
||||
} |
||||
|
||||
private ExtendedGroovyClassLoader createLoader(GroovyCompilerConfiguration configuration) { |
||||
|
||||
ExtendedGroovyClassLoader loader = new ExtendedGroovyClassLoader(configuration.getScope()); |
||||
|
||||
for (URL url : getExistingUrls()) { |
||||
loader.addURL(url); |
||||
} |
||||
|
||||
for (String classpath : configuration.getClasspath()) { |
||||
loader.addClasspath(classpath); |
||||
} |
||||
|
||||
return loader; |
||||
} |
||||
|
||||
private URL[] getExistingUrls() { |
||||
ClassLoader tccl = Thread.currentThread().getContextClassLoader(); |
||||
if (tccl instanceof ExtendedGroovyClassLoader groovyClassLoader) { |
||||
return groovyClassLoader.getURLs(); |
||||
} |
||||
else { |
||||
return new URL[0]; |
||||
} |
||||
} |
||||
|
||||
public void addCompilationCustomizers(CompilationCustomizer... customizers) { |
||||
this.loader.getConfiguration().addCompilationCustomizers(customizers); |
||||
} |
||||
|
||||
/** |
||||
* Compile the specified Groovy sources, applying any |
||||
* {@link CompilerAutoConfiguration}s. All classes defined in the sources will be |
||||
* returned from this method. |
||||
* @param sources the sources to compile |
||||
* @return compiled classes |
||||
* @throws CompilationFailedException in case of compilation failures |
||||
* @throws IOException in case of I/O errors |
||||
* @throws CompilationFailedException in case of compilation errors |
||||
*/ |
||||
public Class<?>[] compile(String... sources) throws CompilationFailedException, IOException { |
||||
|
||||
this.loader.clearCache(); |
||||
List<Class<?>> classes = new ArrayList<>(); |
||||
|
||||
CompilerConfiguration configuration = this.loader.getConfiguration(); |
||||
|
||||
CompilationUnit compilationUnit = new CompilationUnit(configuration, null, this.loader); |
||||
ClassCollector collector = this.loader.createCollector(compilationUnit, null); |
||||
compilationUnit.setClassgenCallback(collector); |
||||
|
||||
for (String source : sources) { |
||||
List<String> paths = ResourceUtils.getUrls(source, this.loader); |
||||
for (String path : paths) { |
||||
compilationUnit.addSource(new URL(path)); |
||||
} |
||||
} |
||||
|
||||
addAstTransformations(compilationUnit); |
||||
|
||||
compilationUnit.compile(Phases.CLASS_GENERATION); |
||||
for (Object loadedClass : collector.getLoadedClasses()) { |
||||
classes.add((Class<?>) loadedClass); |
||||
} |
||||
ClassNode mainClassNode = MainClass.get(compilationUnit); |
||||
|
||||
Class<?> mainClass = null; |
||||
for (Class<?> loadedClass : classes) { |
||||
if (mainClassNode.getName().equals(loadedClass.getName())) { |
||||
mainClass = loadedClass; |
||||
} |
||||
} |
||||
if (mainClass != null) { |
||||
classes.remove(mainClass); |
||||
classes.add(0, mainClass); |
||||
} |
||||
|
||||
return ClassUtils.toClassArray(classes); |
||||
} |
||||
|
||||
@SuppressWarnings("rawtypes") |
||||
private void addAstTransformations(CompilationUnit compilationUnit) { |
||||
Deque[] phaseOperations = getPhaseOperations(compilationUnit); |
||||
processConversionOperations((LinkedList) phaseOperations[Phases.CONVERSION]); |
||||
} |
||||
|
||||
@SuppressWarnings("rawtypes") |
||||
private Deque[] getPhaseOperations(CompilationUnit compilationUnit) { |
||||
try { |
||||
Field field = CompilationUnit.class.getDeclaredField("phaseOperations"); |
||||
field.setAccessible(true); |
||||
return (Deque[]) field.get(compilationUnit); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("Phase operations not available from compilation unit"); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" }) |
||||
private void processConversionOperations(LinkedList conversionOperations) { |
||||
int index = getIndexOfASTTransformationVisitor(conversionOperations); |
||||
conversionOperations.add(index, new CompilationUnit.ISourceUnitOperation() { |
||||
@Override |
||||
public void call(SourceUnit source) throws CompilationFailedException { |
||||
ASTNode[] nodes = new ASTNode[] { source.getAST() }; |
||||
for (ASTTransformation transformation : GroovyCompiler.this.transformations) { |
||||
transformation.visit(nodes, source); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private int getIndexOfASTTransformationVisitor(List<?> conversionOperations) { |
||||
for (int index = 0; index < conversionOperations.size(); index++) { |
||||
if (conversionOperations.get(index).getClass().getName() |
||||
.startsWith(ASTTransformationVisitor.class.getName())) { |
||||
return index; |
||||
} |
||||
} |
||||
return conversionOperations.size(); |
||||
} |
||||
|
||||
/** |
||||
* {@link CompilationCustomizer} to call {@link CompilerAutoConfiguration}s. |
||||
*/ |
||||
private class CompilerAutoConfigureCustomizer extends CompilationCustomizer { |
||||
|
||||
CompilerAutoConfigureCustomizer() { |
||||
super(CompilePhase.CONVERSION); |
||||
} |
||||
|
||||
@Override |
||||
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) |
||||
throws CompilationFailedException { |
||||
|
||||
ImportCustomizer importCustomizer = new SmartImportCustomizer(source); |
||||
List<ClassNode> classNodes = source.getAST().getClasses(); |
||||
ClassNode mainClassNode = MainClass.get(classNodes); |
||||
|
||||
// Additional auto configuration
|
||||
for (CompilerAutoConfiguration autoConfiguration : GroovyCompiler.this.compilerAutoConfigurations) { |
||||
if (classNodes.stream().anyMatch(autoConfiguration::matches)) { |
||||
if (GroovyCompiler.this.configuration.isGuessImports()) { |
||||
autoConfiguration.applyImports(importCustomizer); |
||||
importCustomizer.call(source, context, classNode); |
||||
} |
||||
if (classNode.equals(mainClassNode)) { |
||||
autoConfiguration.applyToMainClass(GroovyCompiler.this.loader, |
||||
GroovyCompiler.this.configuration, context, source, classNode); |
||||
} |
||||
autoConfiguration.apply(GroovyCompiler.this.loader, GroovyCompiler.this.configuration, context, |
||||
source, classNode); |
||||
} |
||||
} |
||||
importCustomizer.call(source, context, classNode); |
||||
} |
||||
|
||||
} |
||||
|
||||
private static class MainClass { |
||||
|
||||
static ClassNode get(CompilationUnit source) { |
||||
return get(source.getAST().getClasses()); |
||||
} |
||||
|
||||
static ClassNode get(List<ClassNode> classes) { |
||||
for (ClassNode node : classes) { |
||||
if (AstUtils.hasAtLeastOneAnnotation(node, "Enable*AutoConfiguration")) { |
||||
return null; // No need to enhance this
|
||||
} |
||||
if (AstUtils.hasAtLeastOneAnnotation(node, "*Controller", "Configuration", "Component", "*Service", |
||||
"Repository", "Enable*")) { |
||||
return node; |
||||
} |
||||
} |
||||
return classes.isEmpty() ? null : classes.get(0); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,81 +0,0 @@
@@ -1,81 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
|
||||
/** |
||||
* Configuration for the {@link GroovyCompiler}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public interface GroovyCompilerConfiguration { |
||||
|
||||
/** |
||||
* Constant to be used when there is no {@link #getClasspath() classpath}. |
||||
*/ |
||||
String[] DEFAULT_CLASSPATH = { "." }; |
||||
|
||||
/** |
||||
* Returns the scope in which the compiler operates. |
||||
* @return the scope of the compiler |
||||
*/ |
||||
GroovyCompilerScope getScope(); |
||||
|
||||
/** |
||||
* Returns if import declarations should be guessed. |
||||
* @return {@code true} if imports should be guessed, otherwise {@code false} |
||||
*/ |
||||
boolean isGuessImports(); |
||||
|
||||
/** |
||||
* Returns if jar dependencies should be guessed. |
||||
* @return {@code true} if dependencies should be guessed, otherwise {@code false} |
||||
*/ |
||||
boolean isGuessDependencies(); |
||||
|
||||
/** |
||||
* Returns true if auto-configuration transformations should be applied. |
||||
* @return {@code true} if auto-configuration transformations should be applied, |
||||
* otherwise {@code false} |
||||
*/ |
||||
boolean isAutoconfigure(); |
||||
|
||||
/** |
||||
* Returns the classpath for local resources. |
||||
* @return a path for local resources |
||||
*/ |
||||
String[] getClasspath(); |
||||
|
||||
/** |
||||
* Returns the configuration for the repositories that will be used by the compiler to |
||||
* resolve dependencies. |
||||
* @return the repository configurations |
||||
*/ |
||||
List<RepositoryConfiguration> getRepositoryConfiguration(); |
||||
|
||||
/** |
||||
* Returns if running in quiet mode. |
||||
* @return {@code true} if running in quiet mode |
||||
*/ |
||||
boolean isQuiet(); |
||||
|
||||
} |
||||
@ -1,38 +0,0 @@
@@ -1,38 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
/** |
||||
* The scope in which a groovy compiler operates. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public enum GroovyCompilerScope { |
||||
|
||||
/** |
||||
* Default scope, exposes groovy.jar (loaded from the parent) and the shared cli |
||||
* package (loaded via groovy classloader). |
||||
*/ |
||||
DEFAULT, |
||||
|
||||
/** |
||||
* Extension scope, allows full access to internal CLI classes. |
||||
*/ |
||||
EXTENSION |
||||
|
||||
} |
||||
@ -1,129 +0,0 @@
@@ -1,129 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.io.File; |
||||
import java.net.URI; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.apache.maven.settings.Profile; |
||||
import org.apache.maven.settings.Repository; |
||||
import org.codehaus.plexus.interpolation.InterpolationException; |
||||
import org.codehaus.plexus.interpolation.Interpolator; |
||||
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource; |
||||
import org.codehaus.plexus.interpolation.RegexBasedInterpolator; |
||||
|
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
||||
import org.springframework.boot.cli.compiler.maven.MavenSettings; |
||||
import org.springframework.boot.cli.compiler.maven.MavenSettingsReader; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Factory used to create {@link RepositoryConfiguration}s. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public final class RepositoryConfigurationFactory { |
||||
|
||||
private static final RepositoryConfiguration MAVEN_CENTRAL = new RepositoryConfiguration("central", |
||||
URI.create("https://repo.maven.apache.org/maven2/"), false); |
||||
|
||||
private static final RepositoryConfiguration SPRING_MILESTONE = new RepositoryConfiguration("spring-milestone", |
||||
URI.create("https://repo.spring.io/milestone"), false); |
||||
|
||||
private static final RepositoryConfiguration SPRING_SNAPSHOT = new RepositoryConfiguration("spring-snapshot", |
||||
URI.create("https://repo.spring.io/snapshot"), true); |
||||
|
||||
private RepositoryConfigurationFactory() { |
||||
} |
||||
|
||||
/** |
||||
* Create a new default repository configuration. |
||||
* @return the newly-created default repository configuration |
||||
*/ |
||||
public static List<RepositoryConfiguration> createDefaultRepositoryConfiguration() { |
||||
MavenSettings mavenSettings = new MavenSettingsReader().readSettings(); |
||||
List<RepositoryConfiguration> repositoryConfiguration = new ArrayList<>(); |
||||
repositoryConfiguration.add(MAVEN_CENTRAL); |
||||
if (!Boolean.getBoolean("disableSpringSnapshotRepos")) { |
||||
repositoryConfiguration.add(SPRING_MILESTONE); |
||||
repositoryConfiguration.add(SPRING_SNAPSHOT); |
||||
} |
||||
addDefaultCacheAsRepository(mavenSettings.getLocalRepository(), repositoryConfiguration); |
||||
addActiveProfileRepositories(mavenSettings.getActiveProfiles(), repositoryConfiguration); |
||||
return repositoryConfiguration; |
||||
} |
||||
|
||||
private static void addDefaultCacheAsRepository(String localRepository, |
||||
List<RepositoryConfiguration> repositoryConfiguration) { |
||||
RepositoryConfiguration repository = new RepositoryConfiguration("local", |
||||
getLocalRepositoryDirectory(localRepository).toURI(), true); |
||||
if (!repositoryConfiguration.contains(repository)) { |
||||
repositoryConfiguration.add(0, repository); |
||||
} |
||||
} |
||||
|
||||
private static void addActiveProfileRepositories(List<Profile> activeProfiles, |
||||
List<RepositoryConfiguration> configurations) { |
||||
for (Profile activeProfile : activeProfiles) { |
||||
Interpolator interpolator = new RegexBasedInterpolator(); |
||||
interpolator.addValueSource(new PropertiesBasedValueSource(activeProfile.getProperties())); |
||||
for (Repository repository : activeProfile.getRepositories()) { |
||||
configurations.add(getRepositoryConfiguration(interpolator, repository)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static RepositoryConfiguration getRepositoryConfiguration(Interpolator interpolator, |
||||
Repository repository) { |
||||
String name = interpolate(interpolator, repository.getId()); |
||||
String url = interpolate(interpolator, repository.getUrl()); |
||||
boolean snapshotsEnabled = false; |
||||
if (repository.getSnapshots() != null) { |
||||
snapshotsEnabled = repository.getSnapshots().isEnabled(); |
||||
} |
||||
return new RepositoryConfiguration(name, URI.create(url), snapshotsEnabled); |
||||
} |
||||
|
||||
private static String interpolate(Interpolator interpolator, String value) { |
||||
try { |
||||
return interpolator.interpolate(value); |
||||
} |
||||
catch (InterpolationException ex) { |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
private static File getLocalRepositoryDirectory(String localRepository) { |
||||
if (StringUtils.hasText(localRepository)) { |
||||
return new File(localRepository); |
||||
} |
||||
return new File(getM2HomeDirectory(), "repository"); |
||||
} |
||||
|
||||
private static File getM2HomeDirectory() { |
||||
String mavenRoot = System.getProperty("maven.home"); |
||||
if (StringUtils.hasLength(mavenRoot)) { |
||||
return new File(mavenRoot); |
||||
} |
||||
return new File(System.getProperty("user.home"), ".m2"); |
||||
} |
||||
|
||||
} |
||||
@ -1,109 +0,0 @@
@@ -1,109 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import groovy.lang.Grab; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.expr.ConstantExpression; |
||||
import org.codehaus.groovy.ast.expr.Expression; |
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext; |
||||
import org.springframework.core.annotation.Order; |
||||
|
||||
/** |
||||
* {@link ASTTransformation} to resolve {@link Grab @Grab} artifact coordinates. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
@Order(ResolveDependencyCoordinatesTransformation.ORDER) |
||||
public class ResolveDependencyCoordinatesTransformation extends AnnotatedNodeASTTransformation { |
||||
|
||||
/** |
||||
* The order of the transformation. |
||||
*/ |
||||
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 300; |
||||
|
||||
private static final Set<String> GRAB_ANNOTATION_NAMES = Collections |
||||
.unmodifiableSet(new HashSet<>(Arrays.asList(Grab.class.getName(), Grab.class.getSimpleName()))); |
||||
|
||||
private final DependencyResolutionContext resolutionContext; |
||||
|
||||
public ResolveDependencyCoordinatesTransformation(DependencyResolutionContext resolutionContext) { |
||||
super(GRAB_ANNOTATION_NAMES, false); |
||||
this.resolutionContext = resolutionContext; |
||||
} |
||||
|
||||
@Override |
||||
protected void processAnnotationNodes(List<AnnotationNode> annotationNodes) { |
||||
for (AnnotationNode annotationNode : annotationNodes) { |
||||
transformGrabAnnotation(annotationNode); |
||||
} |
||||
} |
||||
|
||||
private void transformGrabAnnotation(AnnotationNode grabAnnotation) { |
||||
grabAnnotation.setMember("initClass", new ConstantExpression(false)); |
||||
String value = getValue(grabAnnotation); |
||||
if (value != null && !isConvenienceForm(value)) { |
||||
applyGroupAndVersion(grabAnnotation, value); |
||||
} |
||||
} |
||||
|
||||
private String getValue(AnnotationNode annotation) { |
||||
Expression expression = annotation.getMember("value"); |
||||
if (expression instanceof ConstantExpression constantExpression) { |
||||
Object value = constantExpression.getValue(); |
||||
return (value instanceof String string) ? string : null; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private boolean isConvenienceForm(String value) { |
||||
return value.contains(":") || value.contains("#"); |
||||
} |
||||
|
||||
private void applyGroupAndVersion(AnnotationNode annotation, String module) { |
||||
if (module != null) { |
||||
setMember(annotation, "module", module); |
||||
} |
||||
else { |
||||
Expression expression = annotation.getMembers().get("module"); |
||||
module = (String) ((ConstantExpression) expression).getValue(); |
||||
} |
||||
if (annotation.getMember("group") == null) { |
||||
setMember(annotation, "group", this.resolutionContext.getArtifactCoordinatesResolver().getGroupId(module)); |
||||
} |
||||
if (annotation.getMember("version") == null) { |
||||
setMember(annotation, "version", |
||||
this.resolutionContext.getArtifactCoordinatesResolver().getVersion(module)); |
||||
} |
||||
} |
||||
|
||||
private void setMember(AnnotationNode annotation, String name, String value) { |
||||
ConstantExpression expression = new ConstantExpression(value); |
||||
annotation.setMember(name, expression); |
||||
} |
||||
|
||||
} |
||||
@ -1,55 +0,0 @@
@@ -1,55 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import org.codehaus.groovy.ast.ClassHelper; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
/** |
||||
* Smart extension of {@link ImportCustomizer} that will only add a specific import if a |
||||
* class with the same name is not already explicitly imported. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
class SmartImportCustomizer extends ImportCustomizer { |
||||
|
||||
private SourceUnit source; |
||||
|
||||
SmartImportCustomizer(SourceUnit source) { |
||||
this.source = source; |
||||
} |
||||
|
||||
@Override |
||||
public ImportCustomizer addImport(String alias, String className) { |
||||
if (this.source.getAST().getImport(ClassHelper.make(className).getNameWithoutPackage()) == null) { |
||||
super.addImport(alias, className); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public ImportCustomizer addImports(String... imports) { |
||||
for (String alias : imports) { |
||||
if (this.source.getAST().getImport(ClassHelper.make(alias).getNameWithoutPackage()) == null) { |
||||
super.addImports(alias); |
||||
} |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
} |
||||
@ -1,31 +0,0 @@
@@ -1,31 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler; |
||||
|
||||
import org.codehaus.groovy.transform.ASTTransformation; |
||||
|
||||
/** |
||||
* Marker interface for AST transformations that should be installed automatically from |
||||
* {@code META-INF/services}. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
@FunctionalInterface |
||||
public interface SpringBootAstTransformation extends ASTTransformation { |
||||
|
||||
} |
||||
@ -1,51 +0,0 @@
@@ -1,51 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for the caching infrastructure. |
||||
* |
||||
* @author Stephane Nicoll |
||||
* @since 1.2.0 |
||||
*/ |
||||
public class CachingCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableCaching"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { |
||||
dependencies.add("spring-context-support"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { |
||||
imports.addStarImports("org.springframework.cache", "org.springframework.cache.annotation", |
||||
"org.springframework.cache.concurrent"); |
||||
} |
||||
|
||||
} |
||||
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
import org.springframework.boot.groovy.EnableGroovyTemplates; |
||||
import org.springframework.boot.groovy.GroovyTemplate; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Groovy Templates (outside MVC). |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.1.0 |
||||
*/ |
||||
public class GroovyTemplatesCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableGroovyTemplates"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("groovy.text.TemplateEngine").add("groovy-templates"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addStarImports("groovy.text"); |
||||
imports.addImports(EnableGroovyTemplates.class.getCanonicalName()); |
||||
imports.addStaticImport(GroovyTemplate.class.getName(), "template"); |
||||
} |
||||
|
||||
} |
||||
@ -1,51 +0,0 @@
@@ -1,51 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring JDBC. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class JdbcCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneFieldOrMethod(classNode, "JdbcTemplate", "NamedParameterJdbcTemplate", |
||||
"DataSource"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate").add("spring-boot-starter-jdbc"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addStarImports("org.springframework.jdbc.core", "org.springframework.jdbc.core.namedparam"); |
||||
imports.addImports("javax.sql.DataSource"); |
||||
} |
||||
|
||||
} |
||||
@ -1,54 +0,0 @@
@@ -1,54 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring JMS. |
||||
* |
||||
* @author Greg Turnquist |
||||
* @author Stephane Nicoll |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJms") |
||||
|| AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJmsMessaging"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { |
||||
dependencies.add("spring-jms", "jakarta.jms-api"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { |
||||
imports.addStarImports("jakarta.jms", "org.springframework.jms.annotation", "org.springframework.jms.config", |
||||
"org.springframework.jms.core", "org.springframework.jms.listener", |
||||
"org.springframework.jms.listener.adapter"); |
||||
} |
||||
|
||||
} |
||||
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Rabbit. |
||||
* |
||||
* @author Greg Turnquist |
||||
* @author Stephane Nicoll |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class RabbitCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbit") |
||||
|| AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbitMessaging"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { |
||||
dependencies.add("spring-rabbit"); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { |
||||
imports.addStarImports("org.springframework.amqp.rabbit.annotation", "org.springframework.amqp.rabbit.core", |
||||
"org.springframework.amqp.rabbit.config", "org.springframework.amqp.rabbit.connection", |
||||
"org.springframework.amqp.rabbit.listener", "org.springframework.amqp.rabbit.listener.adapter", |
||||
"org.springframework.amqp.core"); |
||||
} |
||||
|
||||
} |
||||
@ -1,63 +0,0 @@
@@ -1,63 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Batch. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringBatchCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableBatchProcessing"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.batch.core.Job").add("spring-boot-starter-batch"); |
||||
dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate").add("spring-jdbc"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addImports("org.springframework.batch.repeat.RepeatStatus", |
||||
"org.springframework.batch.core.scope.context.ChunkContext", |
||||
"org.springframework.batch.core.step.tasklet.Tasklet", |
||||
"org.springframework.batch.core.configuration.annotation.StepScope", |
||||
"org.springframework.batch.core.configuration.annotation.JobBuilderFactory", |
||||
"org.springframework.batch.core.configuration.annotation.StepBuilderFactory", |
||||
"org.springframework.batch.core.configuration.annotation.EnableBatchProcessing", |
||||
"org.springframework.batch.core.Step", "org.springframework.batch.core.StepExecution", |
||||
"org.springframework.batch.core.StepContribution", "org.springframework.batch.core.Job", |
||||
"org.springframework.batch.core.JobExecution", "org.springframework.batch.core.JobParameter", |
||||
"org.springframework.batch.core.JobParameters", "org.springframework.batch.core.launch.JobLauncher", |
||||
"org.springframework.batch.core.converter.JobParametersConverter", |
||||
"org.springframework.batch.core.converter.DefaultJobParametersConverter"); |
||||
} |
||||
|
||||
} |
||||
@ -1,94 +0,0 @@
@@ -1,94 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2021 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import groovy.lang.GroovyClassLoader; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassHelper; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.classgen.GeneratorContext; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.boot.SpringApplication").add("spring-boot-starter"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addImports("jakarta.annotation.PostConstruct", "jakarta.annotation.PreDestroy", |
||||
"groovy.util.logging.Log", "org.springframework.stereotype.Controller", |
||||
"org.springframework.stereotype.Service", "org.springframework.stereotype.Component", |
||||
"org.springframework.beans.factory.annotation.Autowired", |
||||
"org.springframework.beans.factory.annotation.Value", "org.springframework.context.annotation.Import", |
||||
"org.springframework.context.annotation.ImportResource", |
||||
"org.springframework.context.annotation.Profile", "org.springframework.context.annotation.Scope", |
||||
"org.springframework.context.annotation.Configuration", |
||||
"org.springframework.context.annotation.ComponentScan", "org.springframework.context.annotation.Bean", |
||||
"org.springframework.context.ApplicationContext", "org.springframework.context.MessageSource", |
||||
"org.springframework.core.annotation.Order", "org.springframework.core.io.ResourceLoader", |
||||
"org.springframework.boot.ApplicationRunner", "org.springframework.boot.ApplicationArguments", |
||||
"org.springframework.boot.CommandLineRunner", |
||||
"org.springframework.boot.context.properties.ConfigurationProperties", |
||||
"org.springframework.boot.context.properties.EnableConfigurationProperties", |
||||
"org.springframework.boot.autoconfigure.EnableAutoConfiguration", |
||||
"org.springframework.boot.autoconfigure.SpringBootApplication", |
||||
"org.springframework.boot.context.properties.ConfigurationProperties", |
||||
"org.springframework.boot.context.properties.EnableConfigurationProperties"); |
||||
imports.addStarImports("org.springframework.stereotype", "org.springframework.scheduling.annotation"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyToMainClass(GroovyClassLoader loader, GroovyCompilerConfiguration configuration, |
||||
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode) |
||||
throws CompilationFailedException { |
||||
addEnableAutoConfigurationAnnotation(classNode); |
||||
} |
||||
|
||||
private void addEnableAutoConfigurationAnnotation(ClassNode classNode) { |
||||
if (!hasEnableAutoConfigureAnnotation(classNode)) { |
||||
AnnotationNode annotationNode = new AnnotationNode(ClassHelper.make("EnableAutoConfiguration")); |
||||
classNode.addAnnotation(annotationNode); |
||||
} |
||||
} |
||||
|
||||
private boolean hasEnableAutoConfigureAnnotation(ClassNode classNode) { |
||||
for (AnnotationNode node : classNode.getAnnotations()) { |
||||
String name = node.getClassNode().getNameWithoutPackage(); |
||||
if ("EnableAutoConfiguration".equals(name) || "SpringBootApplication".equals(name)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
} |
||||
@ -1,61 +0,0 @@
@@ -1,61 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Integration. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Artem Bilan |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringIntegrationCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableIntegration") |
||||
|| AstUtils.hasAtLeastOneAnnotation(classNode, "MessageEndpoint"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.integration.config.EnableIntegration") |
||||
.add("spring-boot-starter-integration"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addImports("org.springframework.messaging.Message", "org.springframework.messaging.MessageChannel", |
||||
"org.springframework.messaging.PollableChannel", "org.springframework.messaging.SubscribableChannel", |
||||
"org.springframework.messaging.MessageHeaders", |
||||
"org.springframework.integration.support.MessageBuilder", |
||||
"org.springframework.integration.channel.DirectChannel", |
||||
"org.springframework.integration.channel.QueueChannel", |
||||
"org.springframework.integration.channel.ExecutorChannel", |
||||
"org.springframework.integration.core.MessagingTemplate", |
||||
"org.springframework.integration.config.EnableIntegration"); |
||||
imports.addStarImports("org.springframework.integration.annotation"); |
||||
} |
||||
|
||||
} |
||||
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
import org.springframework.boot.groovy.GroovyTemplate; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring MVC. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringMvcCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "Controller", "RestController", "EnableWebMvc"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.web.servlet.mvc.Controller") |
||||
.add("spring-boot-starter-web"); |
||||
dependencies.ifAnyMissingClasses("groovy.text.TemplateEngine").add("groovy-templates"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addStarImports("org.springframework.web.bind.annotation", |
||||
"org.springframework.web.servlet.config.annotation", "org.springframework.web.servlet", |
||||
"org.springframework.http", "org.springframework.web.servlet.handler", "org.springframework.http", |
||||
"org.springframework.ui", "groovy.text"); |
||||
imports.addStaticImport(GroovyTemplate.class.getName(), "template"); |
||||
} |
||||
|
||||
} |
||||
@ -1,50 +0,0 @@
@@ -1,50 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Retry. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class SpringRetryCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRetry", "Retryable", "Recover"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.retry.annotation.EnableRetry").add("spring-retry", |
||||
"spring-boot-starter-aop"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addStarImports("org.springframework.retry.annotation"); |
||||
} |
||||
|
||||
} |
||||
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Security. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringSecurityCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSecurity", "EnableGlobalMethodSecurity"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses( |
||||
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity") |
||||
.add("spring-boot-starter-security"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addImports("org.springframework.security.core.Authentication", |
||||
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity", |
||||
"org.springframework.security.core.authority.AuthorityUtils") |
||||
.addStarImports("org.springframework.security.config.annotation.web.configuration", |
||||
"org.springframework.security.authentication", |
||||
"org.springframework.security.config.annotation.web", |
||||
"org.springframework.security.config.annotation.web.builders"); |
||||
} |
||||
|
||||
} |
||||
@ -1,73 +0,0 @@
@@ -1,73 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import groovy.lang.GroovyClassLoader; |
||||
import org.codehaus.groovy.ast.AnnotationNode; |
||||
import org.codehaus.groovy.ast.ClassHelper; |
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.ast.expr.ClassExpression; |
||||
import org.codehaus.groovy.classgen.GeneratorContext; |
||||
import org.codehaus.groovy.control.CompilationFailedException; |
||||
import org.codehaus.groovy.control.SourceUnit; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Test. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.1.0 |
||||
*/ |
||||
public class SpringTestCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "SpringBootTest"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.http.HttpHeaders").add("spring-boot-starter-web"); |
||||
} |
||||
|
||||
@Override |
||||
public void apply(GroovyClassLoader loader, GroovyCompilerConfiguration configuration, |
||||
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode) |
||||
throws CompilationFailedException { |
||||
if (!AstUtils.hasAtLeastOneAnnotation(classNode, "RunWith")) { |
||||
AnnotationNode runWith = new AnnotationNode(ClassHelper.make("RunWith")); |
||||
runWith.addMember("value", new ClassExpression(ClassHelper.make("SpringRunner"))); |
||||
classNode.addAnnotation(runWith); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { |
||||
imports.addStarImports("org.junit.runner", "org.springframework.boot.test", |
||||
"org.springframework.boot.test.context", "org.springframework.boot.test.web.client", |
||||
"org.springframework.http", "org.springframework.test.context.junit4", |
||||
"org.springframework.test.annotation") |
||||
.addImports("org.springframework.boot.test.context.SpringBootTest.WebEnvironment", |
||||
"org.springframework.boot.test.web.client.TestRestTemplate"); |
||||
} |
||||
|
||||
} |
||||
@ -1,54 +0,0 @@
@@ -1,54 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring Websocket. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class SpringWebsocketCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSocket", "EnableWebSocketMessageBroker"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.web.socket.config.annotation.EnableWebSocket") |
||||
.add("spring-boot-starter-websocket").add("spring-messaging"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addStarImports("org.springframework.messaging.handler.annotation", |
||||
"org.springframework.messaging.simp.config", "org.springframework.web.socket.handler", |
||||
"org.springframework.web.socket.sockjs.transport.handler", |
||||
"org.springframework.web.socket.config.annotation") |
||||
.addImports("org.springframework.web.socket.WebSocketHandler"); |
||||
} |
||||
|
||||
} |
||||
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
|
||||
import org.codehaus.groovy.ast.ClassNode; |
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||
|
||||
import org.springframework.boot.cli.compiler.AstUtils; |
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||
|
||||
/** |
||||
* {@link CompilerAutoConfiguration} for Spring MVC. |
||||
* |
||||
* @author Dave Syer |
||||
* @author Phillip Webb |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class TransactionManagementCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||
|
||||
@Override |
||||
public boolean matches(ClassNode classNode) { |
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableTransactionManagement"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyDependencies(DependencyCustomizer dependencies) { |
||||
dependencies.ifAnyMissingClasses("org.springframework.transaction.annotation.Transactional").add("spring-tx", |
||||
"spring-boot-starter-aop"); |
||||
} |
||||
|
||||
@Override |
||||
public void applyImports(ImportCustomizer imports) { |
||||
imports.addStarImports("org.springframework.transaction.annotation", "org.springframework.transaction.support"); |
||||
imports.addImports("org.springframework.transaction.PlatformTransactionManager", |
||||
"org.springframework.transaction.support.AbstractPlatformTransactionManager"); |
||||
} |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Classes for auto-configuring the Groovy compiler. |
||||
*/ |
||||
package org.springframework.boot.cli.compiler.autoconfigure; |
||||
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
/** |
||||
* A resolver for artifacts' Maven coordinates, allowing group id, artifact id, or version |
||||
* to be obtained from a module identifier. A module identifier may be in the form |
||||
* {@code groupId:artifactId:version}, in which case coordinate resolution simply extracts |
||||
* the relevant piece from the identifier. Alternatively the identifier may be in the form |
||||
* {@code artifactId}, in which case coordinate resolution uses implementation-specific |
||||
* metadata to resolve the groupId and version. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public interface ArtifactCoordinatesResolver { |
||||
|
||||
/** |
||||
* Gets the group id of the artifact identified by the given {@code module}. Returns |
||||
* {@code null} if the artifact is unknown to the resolver. |
||||
* @param module the id of the module |
||||
* @return the group id of the module |
||||
*/ |
||||
String getGroupId(String module); |
||||
|
||||
/** |
||||
* Gets the artifact id of the artifact identified by the given {@code module}. |
||||
* Returns {@code null} if the artifact is unknown to the resolver. |
||||
* @param module the id of the module |
||||
* @return the artifact id of the module |
||||
*/ |
||||
String getArtifactId(String module); |
||||
|
||||
/** |
||||
* Gets the version of the artifact identified by the given {@code module}. Returns |
||||
* {@code null} if the artifact is unknown to the resolver. |
||||
* @param module the id of the module |
||||
* @return the version of the module |
||||
*/ |
||||
String getVersion(String module); |
||||
|
||||
} |
||||
@ -1,70 +0,0 @@
@@ -1,70 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* {@link DependencyManagement} that delegates to one or more {@link DependencyManagement} |
||||
* instances. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class CompositeDependencyManagement implements DependencyManagement { |
||||
|
||||
private final List<DependencyManagement> delegates; |
||||
|
||||
private final List<Dependency> dependencies = new ArrayList<>(); |
||||
|
||||
public CompositeDependencyManagement(DependencyManagement... delegates) { |
||||
this.delegates = Arrays.asList(delegates); |
||||
for (DependencyManagement delegate : delegates) { |
||||
this.dependencies.addAll(delegate.getDependencies()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public List<Dependency> getDependencies() { |
||||
return this.dependencies; |
||||
} |
||||
|
||||
@Override |
||||
public String getSpringBootVersion() { |
||||
for (DependencyManagement delegate : this.delegates) { |
||||
String version = delegate.getSpringBootVersion(); |
||||
if (version != null) { |
||||
return version; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Dependency find(String artifactId) { |
||||
for (DependencyManagement delegate : this.delegates) { |
||||
Dependency found = delegate.find(artifactId); |
||||
if (found != null) { |
||||
return found; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -1,198 +0,0 @@
@@ -1,198 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A single dependency. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 1.3.0 |
||||
*/ |
||||
public final class Dependency { |
||||
|
||||
private final String groupId; |
||||
|
||||
private final String artifactId; |
||||
|
||||
private final String version; |
||||
|
||||
private final List<Exclusion> exclusions; |
||||
|
||||
/** |
||||
* Create a new {@link Dependency} instance. |
||||
* @param groupId the group ID |
||||
* @param artifactId the artifact ID |
||||
* @param version the version |
||||
*/ |
||||
public Dependency(String groupId, String artifactId, String version) { |
||||
this(groupId, artifactId, version, Collections.emptyList()); |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@link Dependency} instance. |
||||
* @param groupId the group ID |
||||
* @param artifactId the artifact ID |
||||
* @param version the version |
||||
* @param exclusions the exclusions |
||||
*/ |
||||
public Dependency(String groupId, String artifactId, String version, List<Exclusion> exclusions) { |
||||
Assert.notNull(groupId, "GroupId must not be null"); |
||||
Assert.notNull(artifactId, "ArtifactId must not be null"); |
||||
Assert.notNull(version, "Version must not be null"); |
||||
Assert.notNull(exclusions, "Exclusions must not be null"); |
||||
this.groupId = groupId; |
||||
this.artifactId = artifactId; |
||||
this.version = version; |
||||
this.exclusions = Collections.unmodifiableList(exclusions); |
||||
} |
||||
|
||||
/** |
||||
* Return the dependency group id. |
||||
* @return the group ID |
||||
*/ |
||||
public String getGroupId() { |
||||
return this.groupId; |
||||
} |
||||
|
||||
/** |
||||
* Return the dependency artifact id. |
||||
* @return the artifact ID |
||||
*/ |
||||
public String getArtifactId() { |
||||
return this.artifactId; |
||||
} |
||||
|
||||
/** |
||||
* Return the dependency version. |
||||
* @return the version |
||||
*/ |
||||
public String getVersion() { |
||||
return this.version; |
||||
} |
||||
|
||||
/** |
||||
* Return the dependency exclusions. |
||||
* @return the exclusions |
||||
*/ |
||||
public List<Exclusion> getExclusions() { |
||||
return this.exclusions; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) { |
||||
return true; |
||||
} |
||||
if (obj == null) { |
||||
return false; |
||||
} |
||||
if (getClass() == obj.getClass()) { |
||||
Dependency other = (Dependency) obj; |
||||
boolean result = true; |
||||
result = result && this.groupId.equals(other.groupId); |
||||
result = result && this.artifactId.equals(other.artifactId); |
||||
result = result && this.version.equals(other.version); |
||||
result = result && this.exclusions.equals(other.exclusions); |
||||
return result; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + this.groupId.hashCode(); |
||||
result = prime * result + this.artifactId.hashCode(); |
||||
result = prime * result + this.version.hashCode(); |
||||
result = prime * result + this.exclusions.hashCode(); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return this.groupId + ":" + this.artifactId + ":" + this.version; |
||||
} |
||||
|
||||
/** |
||||
* A dependency exclusion. |
||||
*/ |
||||
public static final class Exclusion { |
||||
|
||||
private final String groupId; |
||||
|
||||
private final String artifactId; |
||||
|
||||
Exclusion(String groupId, String artifactId) { |
||||
Assert.notNull(groupId, "GroupId must not be null"); |
||||
Assert.notNull(artifactId, "ArtifactId must not be null"); |
||||
this.groupId = groupId; |
||||
this.artifactId = artifactId; |
||||
} |
||||
|
||||
/** |
||||
* Return the exclusion artifact ID. |
||||
* @return the exclusion artifact ID |
||||
*/ |
||||
public String getArtifactId() { |
||||
return this.artifactId; |
||||
} |
||||
|
||||
/** |
||||
* Return the exclusion group ID. |
||||
* @return the exclusion group ID |
||||
*/ |
||||
public String getGroupId() { |
||||
return this.groupId; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) { |
||||
return true; |
||||
} |
||||
if (obj == null) { |
||||
return false; |
||||
} |
||||
if (getClass() == obj.getClass()) { |
||||
Exclusion other = (Exclusion) obj; |
||||
boolean result = true; |
||||
result = result && this.groupId.equals(other.groupId); |
||||
result = result && this.artifactId.equals(other.artifactId); |
||||
return result; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return this.groupId.hashCode() * 31 + this.artifactId.hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return this.groupId + ":" + this.artifactId; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,48 +0,0 @@
@@ -1,48 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* An encapsulation of dependency management information. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.3.0 |
||||
*/ |
||||
public interface DependencyManagement { |
||||
|
||||
/** |
||||
* Returns the managed dependencies. |
||||
* @return the managed dependencies |
||||
*/ |
||||
List<Dependency> getDependencies(); |
||||
|
||||
/** |
||||
* Returns the managed version of Spring Boot. May be {@code null}. |
||||
* @return the Spring Boot version, or {@code null} |
||||
*/ |
||||
String getSpringBootVersion(); |
||||
|
||||
/** |
||||
* Finds the managed dependency with the given {@code artifactId}. |
||||
* @param artifactId the artifact ID of the dependency to find |
||||
* @return the dependency, or {@code null} |
||||
*/ |
||||
Dependency find(String artifactId); |
||||
|
||||
} |
||||
@ -1,73 +0,0 @@
@@ -1,73 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* {@link ArtifactCoordinatesResolver} backed by |
||||
* {@link SpringBootDependenciesDependencyManagement}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DependencyManagementArtifactCoordinatesResolver implements ArtifactCoordinatesResolver { |
||||
|
||||
private final DependencyManagement dependencyManagement; |
||||
|
||||
public DependencyManagementArtifactCoordinatesResolver() { |
||||
this(new SpringBootDependenciesDependencyManagement()); |
||||
} |
||||
|
||||
public DependencyManagementArtifactCoordinatesResolver(DependencyManagement dependencyManagement) { |
||||
this.dependencyManagement = dependencyManagement; |
||||
} |
||||
|
||||
@Override |
||||
public String getGroupId(String artifactId) { |
||||
Dependency dependency = find(artifactId); |
||||
return (dependency != null) ? dependency.getGroupId() : null; |
||||
} |
||||
|
||||
@Override |
||||
public String getArtifactId(String id) { |
||||
Dependency dependency = find(id); |
||||
return (dependency != null) ? dependency.getArtifactId() : null; |
||||
} |
||||
|
||||
private Dependency find(String id) { |
||||
if (StringUtils.countOccurrencesOf(id, ":") == 2) { |
||||
String[] tokens = id.split(":"); |
||||
return new Dependency(tokens[0], tokens[1], tokens[2]); |
||||
} |
||||
if (id != null) { |
||||
if (id.startsWith("spring-boot")) { |
||||
return new Dependency("org.springframework.boot", id, this.dependencyManagement.getSpringBootVersion()); |
||||
} |
||||
return this.dependencyManagement.find(id); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String getVersion(String module) { |
||||
Dependency dependency = find(module); |
||||
return (dependency != null) ? dependency.getVersion() : null; |
||||
} |
||||
|
||||
} |
||||
@ -1,76 +0,0 @@
@@ -1,76 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.maven.model.Model; |
||||
|
||||
import org.springframework.boot.cli.compiler.dependencies.Dependency.Exclusion; |
||||
|
||||
/** |
||||
* {@link DependencyManagement} derived from a Maven {@link Model}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class MavenModelDependencyManagement implements DependencyManagement { |
||||
|
||||
private final List<Dependency> dependencies; |
||||
|
||||
private final Map<String, Dependency> byArtifactId = new LinkedHashMap<>(); |
||||
|
||||
public MavenModelDependencyManagement(Model model) { |
||||
this.dependencies = extractDependenciesFromModel(model); |
||||
for (Dependency dependency : this.dependencies) { |
||||
this.byArtifactId.put(dependency.getArtifactId(), dependency); |
||||
} |
||||
} |
||||
|
||||
private static List<Dependency> extractDependenciesFromModel(Model model) { |
||||
List<Dependency> dependencies = new ArrayList<>(); |
||||
for (org.apache.maven.model.Dependency mavenDependency : model.getDependencyManagement().getDependencies()) { |
||||
List<Exclusion> exclusions = new ArrayList<>(); |
||||
for (org.apache.maven.model.Exclusion mavenExclusion : mavenDependency.getExclusions()) { |
||||
exclusions.add(new Exclusion(mavenExclusion.getGroupId(), mavenExclusion.getArtifactId())); |
||||
} |
||||
Dependency dependency = new Dependency(mavenDependency.getGroupId(), mavenDependency.getArtifactId(), |
||||
mavenDependency.getVersion(), exclusions); |
||||
dependencies.add(dependency); |
||||
} |
||||
return dependencies; |
||||
} |
||||
|
||||
@Override |
||||
public List<Dependency> getDependencies() { |
||||
return this.dependencies; |
||||
} |
||||
|
||||
@Override |
||||
public String getSpringBootVersion() { |
||||
return find("spring-boot").getVersion(); |
||||
} |
||||
|
||||
@Override |
||||
public Dependency find(String artifactId) { |
||||
return this.byArtifactId.get(artifactId); |
||||
} |
||||
|
||||
} |
||||
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import org.apache.maven.model.Model; |
||||
import org.apache.maven.model.building.DefaultModelProcessor; |
||||
import org.apache.maven.model.io.DefaultModelReader; |
||||
import org.apache.maven.model.locator.DefaultModelLocator; |
||||
|
||||
/** |
||||
* {@link DependencyManagement} derived from the effective pom of |
||||
* {@code spring-boot-dependencies}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.3.0 |
||||
*/ |
||||
public class SpringBootDependenciesDependencyManagement extends MavenModelDependencyManagement { |
||||
|
||||
public SpringBootDependenciesDependencyManagement() { |
||||
super(readModel()); |
||||
} |
||||
|
||||
private static Model readModel() { |
||||
DefaultModelProcessor modelProcessor = new DefaultModelProcessor(); |
||||
modelProcessor.setModelLocator(new DefaultModelLocator()); |
||||
modelProcessor.setModelReader(new DefaultModelReader()); |
||||
|
||||
try { |
||||
return modelProcessor.read(SpringBootDependenciesDependencyManagement.class |
||||
.getResourceAsStream("spring-boot-dependencies-effective-bom.xml"), null); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new IllegalStateException("Failed to build model from effective pom", ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* Classes for dependencies used during compilation. |
||||
*/ |
||||
package org.springframework.boot.cli.compiler.dependencies; |
||||
@ -1,50 +0,0 @@
@@ -1,50 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.grape; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.eclipse.aether.repository.Proxy; |
||||
import org.eclipse.aether.repository.ProxySelector; |
||||
import org.eclipse.aether.repository.RemoteRepository; |
||||
|
||||
/** |
||||
* Composite {@link ProxySelector}. |
||||
* |
||||
* @author Dave Syer |
||||
* @since 1.1.0 |
||||
*/ |
||||
public class CompositeProxySelector implements ProxySelector { |
||||
|
||||
private final List<ProxySelector> selectors; |
||||
|
||||
public CompositeProxySelector(List<ProxySelector> selectors) { |
||||
this.selectors = selectors; |
||||
} |
||||
|
||||
@Override |
||||
public Proxy getProxy(RemoteRepository repository) { |
||||
for (ProxySelector selector : this.selectors) { |
||||
Proxy proxy = selector.getProxy(repository); |
||||
if (proxy != null) { |
||||
return proxy; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -1,71 +0,0 @@
@@ -1,71 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.grape; |
||||
|
||||
import java.io.File; |
||||
import java.util.Arrays; |
||||
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession; |
||||
import org.eclipse.aether.RepositorySystem; |
||||
import org.eclipse.aether.repository.LocalRepository; |
||||
import org.eclipse.aether.repository.LocalRepositoryManager; |
||||
import org.eclipse.aether.repository.ProxySelector; |
||||
import org.eclipse.aether.util.repository.JreProxySelector; |
||||
|
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* A {@link RepositorySystemSessionAutoConfiguration} that, in the absence of any |
||||
* configuration, applies sensible defaults. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DefaultRepositorySystemSessionAutoConfiguration implements RepositorySystemSessionAutoConfiguration { |
||||
|
||||
@Override |
||||
public void apply(DefaultRepositorySystemSession session, RepositorySystem repositorySystem) { |
||||
|
||||
if (session.getLocalRepositoryManager() == null) { |
||||
LocalRepository localRepository = new LocalRepository(getM2RepoDirectory()); |
||||
LocalRepositoryManager localRepositoryManager = repositorySystem.newLocalRepositoryManager(session, |
||||
localRepository); |
||||
session.setLocalRepositoryManager(localRepositoryManager); |
||||
} |
||||
|
||||
ProxySelector existing = session.getProxySelector(); |
||||
if (!(existing instanceof CompositeProxySelector)) { |
||||
JreProxySelector fallback = new JreProxySelector(); |
||||
ProxySelector selector = (existing != null) ? new CompositeProxySelector(Arrays.asList(existing, fallback)) |
||||
: fallback; |
||||
session.setProxySelector(selector); |
||||
} |
||||
} |
||||
|
||||
private File getM2RepoDirectory() { |
||||
return new File(getDefaultM2HomeDirectory(), "repository"); |
||||
} |
||||
|
||||
private File getDefaultM2HomeDirectory() { |
||||
String mavenRoot = System.getProperty("maven.home"); |
||||
if (StringUtils.hasLength(mavenRoot)) { |
||||
return new File(mavenRoot); |
||||
} |
||||
return new File(System.getProperty("user.home"), ".m2"); |
||||
} |
||||
|
||||
} |
||||
@ -1,107 +0,0 @@
@@ -1,107 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.cli.compiler.grape; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.eclipse.aether.artifact.DefaultArtifact; |
||||
import org.eclipse.aether.graph.Dependency; |
||||
import org.eclipse.aether.graph.Exclusion; |
||||
import org.eclipse.aether.util.artifact.JavaScopes; |
||||
|
||||
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver; |
||||
import org.springframework.boot.cli.compiler.dependencies.CompositeDependencyManagement; |
||||
import org.springframework.boot.cli.compiler.dependencies.DependencyManagement; |
||||
import org.springframework.boot.cli.compiler.dependencies.DependencyManagementArtifactCoordinatesResolver; |
||||
|
||||
/** |
||||
* Context used when resolving dependencies. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 1.1.0 |
||||
*/ |
||||
public class DependencyResolutionContext { |
||||
|
||||
private final Map<String, Dependency> managedDependencyByGroupAndArtifact = new HashMap<>(); |
||||
|
||||
private final List<Dependency> managedDependencies = new ArrayList<>(); |
||||
|
||||
private DependencyManagement dependencyManagement = null; |
||||
|
||||
private ArtifactCoordinatesResolver artifactCoordinatesResolver; |
||||
|
||||
private String getIdentifier(Dependency dependency) { |
||||
return getIdentifier(dependency.getArtifact().getGroupId(), dependency.getArtifact().getArtifactId()); |
||||
} |
||||
|
||||
private String getIdentifier(String groupId, String artifactId) { |
||||
return groupId + ":" + artifactId; |
||||
} |
||||
|
||||
public ArtifactCoordinatesResolver getArtifactCoordinatesResolver() { |
||||
return this.artifactCoordinatesResolver; |
||||
} |
||||
|
||||
public String getManagedVersion(String groupId, String artifactId) { |
||||
Dependency dependency = getManagedDependency(groupId, artifactId); |
||||
if (dependency == null) { |
||||
dependency = this.managedDependencyByGroupAndArtifact.get(getIdentifier(groupId, artifactId)); |
||||
} |
||||
return (dependency != null) ? dependency.getArtifact().getVersion() : null; |
||||
} |
||||
|
||||
public List<Dependency> getManagedDependencies() { |
||||
return Collections.unmodifiableList(this.managedDependencies); |
||||
} |
||||
|
||||
private Dependency getManagedDependency(String group, String artifact) { |
||||
return this.managedDependencyByGroupAndArtifact.get(getIdentifier(group, artifact)); |
||||
} |
||||
|
||||
public void addManagedDependencies(List<Dependency> dependencies) { |
||||
this.managedDependencies.addAll(dependencies); |
||||
for (Dependency dependency : dependencies) { |
||||
this.managedDependencyByGroupAndArtifact.put(getIdentifier(dependency), dependency); |
||||
} |
||||
} |
||||
|
||||
public void addDependencyManagement(DependencyManagement dependencyManagement) { |
||||
for (org.springframework.boot.cli.compiler.dependencies.Dependency dependency : dependencyManagement |
||||
.getDependencies()) { |
||||
List<Exclusion> aetherExclusions = new ArrayList<>(); |
||||
for (org.springframework.boot.cli.compiler.dependencies.Dependency.Exclusion exclusion : dependency |
||||
.getExclusions()) { |
||||
aetherExclusions.add(new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*")); |
||||
} |
||||
Dependency aetherDependency = new Dependency(new DefaultArtifact(dependency.getGroupId(), |
||||
dependency.getArtifactId(), "jar", dependency.getVersion()), JavaScopes.COMPILE, false, |
||||
aetherExclusions); |
||||
this.managedDependencies.add(0, aetherDependency); |
||||
this.managedDependencyByGroupAndArtifact.put(getIdentifier(aetherDependency), aetherDependency); |
||||
} |
||||
this.dependencyManagement = (this.dependencyManagement != null) |
||||
? new CompositeDependencyManagement(dependencyManagement, this.dependencyManagement) |
||||
: dependencyManagement; |
||||
this.artifactCoordinatesResolver = new DependencyManagementArtifactCoordinatesResolver( |
||||
this.dependencyManagement); |
||||
} |
||||
|
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue