SpringNative etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
SpringNative etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

9 Ağustos 2023 Çarşamba

SpringNative Buildpacks

Giriş
Açıklaması şöyle
Spring Boot includes buildpack support for native images directly for Maven . This means we can just type a single command and quickly get a sensible image into our locally running Docker daemon. The resulting image doesn’t contain a JVM, instead the native image is compiled statically. This leads to smaller images. There are 3 types of buildpack image available for use:
1. paketobuildpacks/builder:tiny
2. paketobuildpacks/builder:base
3. paketobuildpacks/builder:full
spring-boot-starter-parent Kullanıyorsak
Açıklaması şöyle
The spring-boot-starter-parent declares a native profile that configures the executions that need to run in order to create a native image. GraalVM Native Support dependency generate this in pom. You can activate profiles using the -P flag on the command line. 
Şöyle yaparız
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.1.0</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
Sonra şöyle yaparız
$ mvn -Pnative spring-boot:build-image
Yapılandırılan Docker image'i çalıştırmak için şöyle yaparız
docker run --rm -p 8080:8080 native:0.0.1-SNAPSHOT
spring-boot-starter-parent Kullanmıyorsak
Açıklaması şöyle
If you don’t want to use spring-boot-starter-parent you’ll need to configure executions for the process-aot goal from Spring Boot’s plugin and the add-reachability-metadata goal from the Native Build Tools plugin.
NoClassDefFoundError Hataları
Bu durumda reflection-config.json dosyasını tekrar üretmek gerekir



1 Ağustos 2023 Salı

Spring 3 İçin SpringNative @RegisterReflectionForBinding Anotasyonu

Giriş
Açıklaması şöyle
Registering classes for which you want to use reflection is just as easy, you either use @RegisterReflectionForBinding annotation or you can do the same through the RuntimeHintsRegistrar with more flexible configuration:

Moreover, you can use the @RegisterReflectionForBinding annotation anywhere (services, methods and etc.), but we decided to use only the RuntimeHintsRegistrar to contain all the AOT configurations in one place and not search through the code.
Örnek
Jackson için şöyle yaparız
@RegisterReflectionForBinding({MyClass.class, MyClass2.class})
Örnek
Şöyle yaparız
@Configuration
@RegisterReflectionForBinding({CustomMessage.class, CustomMessage.Status.class})
@ImportRuntimeHints(AppConfiguration.AppRuntimeHintsRegistrar.class)
public class AppConfiguration {

  public static class AppRuntimeHintsRegistrar implements RuntimeHintsRegistrar {

   @Override
   public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
     hints.reflection()
       .registerType(
         CustomMessage.class,
         PUBLIC_FIELDS, INVOKE_PUBLIC_METHODS, INVOKE_PUBLIC_CONSTRUCTORS
         ).registerType(
           CustomMessage.Status.class,
           PUBLIC_FIELDS, INVOKE_PUBLIC_METHODS, INVOKE_PUBLIC_CONSTRUCTORS
         );
      }
  }
}

Spring 3 İçin SpringNative @ImportRuntimeHints Anotasyonu

Giriş
Açıklaması şöyle
In Spring Native (as we did in Quarkus), we need to specify some classes for reflection, serialization, proxy usage, etc. Spring calls all of these <hints>. The problem is that the GraalVM, at compilation time, cannot recognize every class in our project that must be used for reflection at runtime. For this reason, these frameworks (Spring and Quarkus) have annotations that we can use con classes to specify reflection at compilation time to GraalVM.
Örnek
Şöyle yaparız
@SpringBootApplication
@ImportRuntimeHints(LdapServiceApplication.MyRuntimeHints.class)
public class LdapServiceApplication {

 public static void main(String[] args) {
  SpringApplication.run(LdapServiceApplication.class, args);
 }

 @Bean
 public RestTemplate restTemplate() {
  return new RestTemplate();
 }

 @PostConstruct
 void postConstruct() {
  System.setProperty("javax.net.ssl.trustStore", 
    System.getProperty("javax.net.ssl.trustStore"));
  System.setProperty("javax.net.ssl.trustStorePassword", 
    System.getProperty("javax.net.ssl.trustStorePassword"));
 }

 static class MyRuntimeHints implements RuntimeHintsRegistrar {

  @Override
  public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
   // Register serialization
   hints.serialization().registerType(HashMap.class).registerType(LinkedList.class);
   hints.reflection().registerType(TypeReference.of("javax.net.ssl.SSLSocketFactory"),
     builder -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
   hints.resources().registerPattern("db/migration/*.sql");
  }
}
Örnek
Şöyle yaparız
import org.postgresql.util.PGobject;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

public class PostgresRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        hints.reflection().registerType(PGobject.class, MemberCategory.values());
    }
}


import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.jdbcjobstore.JobStoreSupport;
import org.quartz.impl.jdbcjobstore.JobStoreTX;
import org.quartz.impl.jdbcjobstore.PostgreSQLDelegate;
import org.quartz.utils.HikariCpPoolingConnectionProvider;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

public class QuartzRuntimeHints implements RuntimeHintsRegistrar {

  @Override
  public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
    hints.reflection().registerType(JobStoreSupport.class, MemberCategory.values());
    hints.reflection().registerType(JobStoreTX.class, MemberCategory.values());
    hints.reflection().registerType(StdSchedulerFactory.class, MemberCategory.values());
    hints.reflection().registerType(HikariCpPoolingConnectionProvider.class, MemberCategory.values());
    hints.reflection().registerType(PostgreSQLDelegate.class, MemberCategory.values());
  }
}

26 Temmuz 2023 Çarşamba

SpringNative reflection-config.json Dosyası

Giriş
Açıklaması şöyle
With a clean or very small Spring Boot application it might work out of the box. However for most applications it will not work in this way because of a GraalVM reflection incompatibility.

In this case, there will be error notifications such as the following:

Warning: Could not resolve org.h2.Driver for reflection configuration. Reason: java.lang.ClassNotFoundException: org.h2.Driver.
Bir başka açıklama şöyle. Yani GraalVM sadece statik analiz ile sınıfları bulmaya çalışıyor, ama Java'da çok fazla reflection kullanıldığı için GraalVM'in tüm kodları bulma şansı yok. Bu yüzden java.lang.NoClassDefFoundError: Could not initialize class ... veya ClassNotFoundException gibi hatalar alıyoruz
When you use Native Image to build native executables it only includes the elements reachable from your application entry point, its dependent libraries, and JDK classes discovered through static analysis. However, the reachability of some elements (such as classes, methods, or fields) may not be discoverable due to Java’s dynamic features including reflection, resource access, dynamic proxies, and serialization. If an element is not reachable, it is not included in the generated executable at build time, which can lead to failures at run time. Native Image has built-in metadata for JDK classes but user code and dependencies may use dynamic features of Java that are undiscoverable by the Native Image analysis. For this reason, Native Image accepts additional reachability metadata in the form of JSON files. Since this metadata is specific to a specific code base, the JSON files providing the corresponding metadata can be shared for libraries and frameworks. This repository is a centralized place for sharing such files for libraries and frameworks that do not provide built-in metadata yet. It is also used to retrofit metadata for older versions of libraries and frameworks.

Çıktı JSON Dosyaları
reflection kullanan sınıfları reflection-config.json dosyasına yazmak gerekir. Dosyanın yolu şöyle
/src/main/resources/META-INF/native-image/reflect-config.json
Dosyayı Üretmek
1. java ... komutu ile uygulama çalıştırılır
2. Kodun çeşitli yerlerinin koşması sağlanır. Örneğin uygulamaya REST istekleri gönderiririz
3. Ctrl + C ile agent sonlandırılır. Agent çıktıyı dosyalara yazar

config-output-dir seçeneği
Normalde dosyaların src/main/resources/META-INF/native-image/ dizininde olması gerekir ama bir sebepten farklı bir hedef dizin vermek gerekirse bu seçenek kullanılır.

Örnek
Şöyle yaparız
java -agentlib:native-image-agent=config-output-dir=target/ \
  -jar native-0.0.1-SNAPSHOT.jar 
target/target dizini altında şu dosyalar üretilir
proxy-config.json
reflect-config.json
resource-config.json

config-merge-dir seçeneği
Örnek
Şöyle yaparız. config-merge-dir seçeneği ile mevcut dosyaya ekleme yapılır
java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image \
  -jar target/my-application-1.0.0-SNAPSHOT.jar
Açıklaması şöyle
This command will generate in the folder the following files:

jni-config.json
predefined-classes-config.json
proxy-config.json
reflect-config.json
resource-config.json
serialization-config.json
Açıklaması şöyle
With the generated metadata and the fixed initialization time of the classes, the native image build should be successful. Nonetheless, at runtime there could come up more errors. The most common one is the ClassNotFoundException. That means that the configuration in the reflect-config.json is incomplete and you should add the class. Another similar error is a FileNotFoundException because a file could not be located in the classpath. This means that the required file is missing in the resource-config.json.
Eğer META-INF/native-image yerine başka bir dizin kullanmak istersek şöyle yaparız
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
  <buildArgs>
    <buildArg>-H:ConfigurationResourceRoots=path/to/resources/</buildArg>
  </buildArgs>
</configuration>

Dosyanın İçin Nasıldır
Örnek
Şöyle yaparız
[
  {
    "name":"org.h2.Driver"
  }
]
Örnek
Şöyle yaparız
[
{ "name": "kotlin.reflect.jvm.internal.ReflectionFactoryImpl", "allDeclaredConstructors":true }, { "name": "kotlin.KotlinVersion", "allPublicMethods": true, "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name": "kotlin.KotlinVersion[]" }, { "name": "kotlin.KotlinVersion$Companion" }, { "name": "kotlin.KotlinVersion$Companion[]" }, { "name": "kotlin.internal.jdk8.JDK8PlatformImplementations", "allPublicMethods": true, "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true } ]

20 Şubat 2023 Pazartesi

Spring 3 İçin SpringNative - GraalVM

Giriş
Spring 3 için açıklaması şöyle
It started as an experimental module called Spring Native, to be added to Spring Boot 2.7 apps. But with Spring Boot 3.0, GraalVM support has been brought into the portfolio projects themselves.

No Spring Native project needed.
Şöyle yaparız
For AOT generation, there's no need to include separate plugins, we can just use a new goal of the spring-boot-maven-plugin

mvn spring-boot:aot-generateCopy
1. GraalVM kurulur
2. Projeye GraalVM Native Support eklenir
3. native-maven-plugin eklenir
Örnek
Şöyle yaparız
<build>
  <plugins>
    ...
    <plugin>
      <groupId>org.graalvm.buildtools</groupId>
      <artifactId>native-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>
Örnek
Tüm pom.xml şöyle. native-maven-plugin bazen farklı bir profile'a da ekleniyor. 
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.1</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>demo</name>
  <description>Demo project for Spring Boot</description>
  <properties>
    <java.version>17</java.version>
  </properties>
  <dependencies>
    ...
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.hibernate.orm.tooling</groupId>
	<artifactId>hibernate-enhance-maven-plugin</artifactId>
	<version>${hibernate.version}</version>
	<executions>
	  <execution>
	    <id>enhance</id>
	      <goals>
	        <goal>enhance</goal>
	      </goals>
	      <configuration>
	        <enableLazyInitialization>true</enableLazyInitialization>
		<enableDirtyTracking>true</enableDirtyTracking>
		<enableAssociationManagement>true</enableAssociationManagement>
	      </configuration>
	    </execution>
	  </executions>
	</plugin>
      <plugin>
        <groupId>org.graalvm.buildtools</groupId>
	<artifactId>native-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
4. mvn clean package -Pnative çalıştırılır. Açıklaması şöyle. AOT kısaltması Ahead-of-Time anlamına gelir.
Maven has three different goals for AOT processing and building the image:

mvn spring-boot:process-aot

mvn spring-boot:process-test-aot

mvn spring-boot:build-image

But these three commands are combined in the mvn clean package -Pnative.

Quick tip: the profile native is predefined in Spring Boot 3 for the native image creation. The same applies to the profile nativeTest as a testing profile.
Örneğin ./target/demo diye bir dosya oluşur. Bu çalıştırılır
veya şöyle yapılir
./mvnw -Pnative native:compile

./target/<native-executable>
RuntimeHints
@ImportRuntimeHints Anotasyonu yazısına taşıdım

@RegisterReflectionForBinding Anotasyonu

AOT
Şöyle yaparız
$ java -Dspring.aot.enabled=true -jar ldap-service.jar



4 Mayıs 2021 Salı

SpringNative

Giriş
Spring 3 ile bu yazıyı iki kısma ayırmak gerekti.

1. Spring 3
Spring 3 SpringNative yazısına taşıdım

2. Spring 3'ten Önce
Daha önceki Spring sürümler için açıklama şöyle
Spring Boot offers two alternatives to create native binaries:
1. A system-dependent binary: this approach requires a local GraalVM installation with the native-image extension. It will create a non-cross-platform system-dependent binary.
For this, Spring Boot has a dedicated profile:
./mvnw -Pnative package

2. A Docker image: this approach builds a containerized version of the application. It requires a local image build, e.g., Docker. Internally, it leverages CNCF Buildpacks (but doesn’t require pack).
Spring Boot provides a Maven target for this:
./mvnw spring-boot:native-image
Eğer yerel GraalVM kurulumunu seçeceksek ilave olarak native-image extension da kurulmalıdır

Her iki yöntem için de 
1. dependency olarak spring-native eklenir. 
2. plugin  olarak şu eklenir
maven için : spring-aot-maven-plugin 
gradle için :  org.springframework.experimental.aot
Açıklaması şöyle
The Spring AOT plugin will automatically be run in the build pipeline to create a special Spring Boot JAR which is needed to run when compiled to a native image. It will try to create all needed reachability configurations for your program to work correctly as a native image.
reflection-config.json Dosyası
SpringNative reflection-config.json Dosyası yazısına taşıdım

Maven
Örnek
Şöyle yaparız
<dependency>
  <groupId>org.springframework.experimental</groupId>
  <artifactId>spring-native</artifactId>
  <version>${spring-native.version}</version>
</dependency>
<plugin>
  <groupId>org.springframework.experimental</groupId>
  <artifactId>spring-aot-maven-plugin</artifactId>
  <version>${spring-native.version}</version>
  <executions>
    <execution>
      <id>test-generate</id>
      <goals>
        <goal>test-generate</goal>
      </goals>
    </execution>
    <execution>
      <id>generate</id>
        <goals>
          <goal>generate</goal>
        </goals>
      </execution>
  </executions>
</plugin>
Gradle
Örnek
Şöyle yaparız
plugins {
  id 'org.springframework.boot' version '2.6.4'
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
  id 'java'
  id 'org.springframework.experimental.aot' version '0.11.3'
}

group = 'com.springnative.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
  maven { url 'https://repo.spring.io/release' }
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
  useJUnitPlatform()
}

tasks.named('bootBuildImage') {
  builder = 'paketobuildpacks/builder:tiny'
  environment = ['BP_NATIVE_IMAGE': 'true']
}
Şöyle yaparız
./gradlew nativeRun
build-image seçeneği
Projeyi maven ile derlesek bir tane docker image üretir. Bunu derleyip çalıştırmak için şöyle yaparız
$ mvn clean package
$ mvn spring-boot:build-image

$ docker run --name spring-native-example -p 8080:8080 spring-native-example:0.0.1-SNAPSHOT
Örnek
Şöyle yaparız
//Build a normal Spring Boot image
mvn spring-boot:build-image -DskipTests

//Build a Spring Boot native image
mvn -Pnative spring-boot:build-image -DskipTests
native-image seçeneği
Şöyle yaparız
./mvnw spring-boot:native-image
@TypeHint Anotasyonu
Kodda reflection configuration belirtmeyi sağlar
Örnek - AccessBits.FULL_REFLECTION 
Şöyle yaparız
@SpringBootApplication
@NativeHint(options = ["--enable-https"])                        <1>
@TypeHint(
    types = [
        Model::class, Data::class, Result::class, Thumbnail::class,
        Collection::class, Resource::class, Url::class, URI::class
    ],
    access = AccessBits.FULL_REFLECTION                          <2>
)
class BootNativeApplication {...}
Örnek
Şöyle yaparız
@TypeHint(
  types = [News::class],
  access = [TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.PUBLIC_METHODS]
)
@SpringBootApplication
class SpringBootKotlinReactiveApplication