25 Aralık 2023 Pazartesi

SpringDoc OpenAPI YAML Çıktısı

Giriş
OpenAPI çıktıyı yaml olarak görmek için şu adrese gideriz 
http://localhost:8080/v3/api-docs.yaml
Çıktı Bileşenleri
1. Metadata Sections
2. Servers and Tags Sections
3. Components Section
4. Path Section

1. Metadata Sections
Açıklaması şöyle
1. openapi — The openapi section indicates the version of OAS used. For example, “3.0.3” signifies major version 3 with patch version 3.
2. info — The info section contains metadata about the API, such as its title, description, terms of service, contact details, license information, and version.
3. externalDocs — The externalDocs section provides a link to extended documentation related to the API, including a description and URL.
Örnek
openapi: 3.0.3

info:
  title: Sample Ecommerce App API
  description: |
    This is a sample ecommerce app API
  termsOfService: https://github.com/someUserId/someProject/blob/main/LICENSE
  contact:
    name: Ecommerce Support
    url: https://www.ecomm.com
    email: support@ecomm.com
  license:
    name: MIT
    url: https://github.com/someUserId/someProject/blob/main/LICENSE
  version: 1.0.0

externalDocs:
  description: Any document link you want to generate along with API.
  url: http://swagger.io
2. Servers and Tags Sections
Açıklaması şöyle
4. servers — The servers section lists the servers hosting the API, facilitating interactive documentation through tools like Swagger UI.
5. tags — The tags section groups operations related to specific resources, allowing for better organization and grouping in generated code.
Örnek
servers:
  - url: https://ecommerce.swagger.io/v2

tags:
  - name: cart
    description: Everything about cart
    externalDocs:
      description: Find out more (extra document link)
      url: http://swagger.io
  - name: order
    description: Operation about orders
3. Components Section
Açıklaması şöyle
The components section is used to define models and reusable components, such as request bodies, responses, and schemas. 
Örnek
components:
  schemas:
    Cart:
      description: Shopping Cart of the user
      type: object
      properties:
        customerId:
          description: Id of the customer who possesses the cart
          type: string
        items:
          description: Collection of items in cart.
          type: array
          items:
            $ref: '#/components/schemas/Item'
4. Path Section
Açıklaması şöyle
The paths section defines API endpoints, including the URI, HTTP methods, parameters, responses, and more.
Örnek
paths:
  /api/v1/carts/{customerId}:
    get:
      tags:
        - cart
      summary: Returns the shopping cart
      description: Returns the shopping cart of a given customer
      operationId: getCartByCustomerId
      parameters:
        - name: customerId
          in: path
          description: Customer Identifier
          required: true
          schema:
            type: string
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Cart'
        404:
          description: Given customer ID doesn't exist
          content: {}


13 Aralık 2023 Çarşamba

spring-boot-maven-plugin repackage Goal - Fat Jar Oluşturur

Giriş
Spring boot uygulamasını paketlemek için gerekir.  Açıklaması şöyle.
Unfortunately, if we are working with a jar package, the basic Maven package goal doesn't include any of the external dependencies.

This means that we can use it only as a library in a bigger project.

To circumvent this limitation, we need to leverage the Maven Spring Boot plugin repackage goal to run our jar/war as a stand-alone application.
Maven 3.2 gerektirir. Açıklaması şöyle.
The Spring Boot Maven Plugin provides Spring Boot support in Maven, allowing you to package executable jar or war archives and run an application “in-place”. To use it you must be using Maven 3.2 (or better).
Eğer repackage goal maven package lifecycle'a bağlı değilse çalıştırmak için şöyle yaparız. Difference Between spring-boot:repackage and Maven package yazısına bakılabilir.
mvn package spring-boot:repackage
Bağlıysa sadece şöyle yaparız
mvn package
Bağlamak için şöyle yaparız. Bu en kullanılabilecek en basit hali.
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <version>2.4.1</version>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
    </execution>
  </executions>
</plugin>
1. repackage Goal - Fat Jar Yapısı
Eğer fat jar'ı açarsak BOOT-INF dizininde kendi kodlarımızı görebiliriz. org ile başlayan dizinde de spring kodları var. Şeklen şöyle
|__BOOT-INF
|  |__classes //1
|  |__ lib //2
|__META-INF
|  |__ MANIFEST.INF
|__org
   |__ springframework
        |__ loader //3
Açıklaması şöyle
1. Project compiled classes
2. JAR dependencies
3. Spring Boot class-loading classes
Manifest dosyasının için şöyledir
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.foo.executable.jar.ExecutableJarApplication
mainClass Tag
Örnek
Eğer kodumuzda birden fazla main metodu varsa başlangıç olanını belirtmek için kullanırız. configuration tag'i için açıklama şöyle.
Your existing archive will be enhanced by Spring Boot during the package phase. The main class that you want to launch can either be specified using a configuration option, or by adding a Main-Class attribute to the manifest in the usual way. If you don’t specify a main class the plugin will search for a class with a public static void main(String[] args) method.
Şöyle yaparız.
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
      <configuration>
        <mainClass>gpdi.MyApp</mainClass>
      </configuration>
    </execution>
  </executions>
</plugin>
classifier Tag
Örnek

Şöyle yaparız.
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <version>${spring.boot.version}</version>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
      <configuration>
        <classifier>exec</classifier>
      </configuration>
    </execution>
  </executions>
</plugin>

4 Aralık 2023 Pazartesi

SpringMVC SpringServletContainerInitializer

Giriş
Şu satırı dahil ederiz
import org.springframework.web.SpringServletContainerInitializer;
Açıklaması şöyle. Yani Tomcat ServletContainerInitializer arayüzünü gerçekleştiren sınıflar arıyor. Bu sınıflardan bir tanesi de SpringServletContainerInitializer
Tomcat is searching for ServletContainerInitializer class at all the jar files included in the project if available. This class has to be stored exactly inside javax.servlet.ServiceContainerInitializer file which is placed in META-INF/services directory in each jar file.
Kalıtım şöyle
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
Açıklaması şöyle. Yani  jakarta.servlet.annotation.HandlesTypes anotasyonu ile jakarta.servlet.ServletContainerInitializer gerçekleştirimi hangi arayüzler ile ilgilendiğini Tomcat'e bildirir
In the ServletContainerInitializer, there is an annotation assigned called @HandlesTypes. The implementer can assign any class for it and Tomcat searches all the classes that have been extended or implemented to the assigned class. 
Böylece WebApplicationInitializer başlatılır

30 Kasım 2023 Perşembe

SpringContext @AliasFor Anotasyonu

Giriş
Şu satırı dahil ederiz
import org.springframework.core.annotation.AliasFor;
Bir açıklama burada


28 Kasım 2023 Salı

SpringScheduling Shedlock KeepAliveLockProvider Sınıfı

Giriş
Şu satırı dahil ederiz
import net.javacrumbs.shedlock.support..KeepAliveLockProvider;
Açıklaması şöyle
KeepAliveLockProvider extends the lock in the middle of the lockAtMostFor interval. For example, if the lockAtMostFor is 10 minutes the lock is extended every 5 minutes for 10 minutes until the lock is released. Please note that the minimal lockAtMostFor time supported by this provider is 30s. The scheduler is used only for the lock extension, single thread should be enough.
Örnek
Şöyle yaparız
@Configuration
public class ShedlockConfiguration {

  @Bean
  public LockProvider lockProvider(DataSource dataSource) {
    return new KeepAliveLockProvider(
      getJdbcTemplateLockProvider(dataSource),
      Executors.newSingleThreadScheduledExecutor()
    );
  }

  private JdbcTemplateLockProvider getJdbcTemplateLockProvider(DataSource dataSource) {
    return new JdbcTemplateLockProvider(
      JdbcTemplateLockProvider.Configuration.builder()
        .withJdbcTemplate(new JdbcTemplate(dataSource))
        .withDbUpperCase(true)
        .usingDbTime()
        .build()
      );
  }
}




9 Kasım 2023 Perşembe

SpringData ElasticSearch ReactiveElasticsearchClient Arayüzü

Giriş
Şu satırı dahil ederiz 
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
Örnek
Şöyle yaparız
@Configuration
public class ReactiveRestClientConfig extends AbstractReactiveElasticsearchConfiguration {
  @Override
  public ReactiveElasticsearchClient reactiveElasticsearchClient() {
    ClientConfiguration clientConfiguration = ClientConfiguration.builder()
      .connectedTo("localhost:9200")
      .build();
    return ReactiveRestClients.create(clientConfiguration);
  }
}

SpringWebFlux Mono.thenReturn metodu

Giriş
İş bittikten sonra farklı bir sonuç dönmek içindir

Örnek
Şöyle yaparız
@Override
public Mono<String> deleteStudent(String id) {
  return studentRepository.deleteById(id)
    .thenReturn("Student deleted successfully!");
}


8 Kasım 2023 Çarşamba

Spring Amqp RabbitMQ Dead Letter Queue

Örnek
Şöyle yaparız
@Configuration
public class RabbitMQConfig { public static final String QUEUE_NAME = "queue-name"; public static final String DLQ_NAME = "dlq-name"; public static final String DLX_NAME = "dlx-name"; @Bean public DirectExchange directExchange() { return new DirectExchange(DIRECT_EXCHANGE); } @Bean public Queue queue() { return new Queue(QUEUE_NAME, true); // Declare the queue as durable } @Bean public Binding binding() { return BindingBuilder.bind(queue()).to(directExchange()).with(QUEUE_NAME); } // DLQ create @Bean public Queue deadLetterQueue() { return new Queue(DLQ_NAME, true); // Declare the DLQ as durable } @Bean public DirectExchange deadLetterExchange() { return new DirectExchange(DLX_NAME); } @Bean public Binding deadLetterBinding() { return BindingBuilder.bind(deadLetterQueue()) .to(deadLetterExchange()).with(DLQ_NAME); } @Bean public Binding queueToDeadLetterExchangeBinding() { return BindingBuilder.bind(queue()) .to(deadLetterExchange()).with(QUEUE_NAME); } }
Şöyle yaparız. Burada DLQ'ya kodla gönderiliyor.
@RabbitListener(queues = "queue-name")
public void handleMessage(String message) {
  try {
    // Process the incoming message
    if (someCondition) {
      throw new Exception("Simulated exception");
    }
    // Message processing succeeded
  } catch (Exception e) {
    // Handle the exception or log it
    System.err.println("Error processing message: " + e.getMessage());
            
    // Send the message to the DLQ
    rabbitTemplate.send("direct.exchange", "dlq-name", new Message(message.getBytes()));
  }
}


SpringData JPA ScrollAPI

Giriş
ScrollAPI aslında Spring Data Commons ile geliyor. SpringData JPA bu bağımlılığı getirdiği için bir şey yapmaya gerek yok

Offset-based scrolling
OffsetScrollPosition  veya WindowIterator sınıfı ile kullanılır. Açıklaması şöyle
Offset scrolling works like pagination, which returns expected results by skipping a certain number of records from a large result. While we only see a portion of the requested results, the server needs to build the full result, which causes additional load.

Örnek
Şöyle  yaparız. Ben sadece bazı açıklamalar ekledim. Window sınıfı offsetleri takip ediyor. Kullanım olarak Window sınıfı Iterator gibi. Window.hasNext() çağrısı yapmak lazım
public List<BookReview> getBooksUsingOffset(String rating) {
  // To keep track of the position in the result set.
  OffsetScrollPosition offset = ScrollPosition.offset();

  // Retrieves the first 5 books with the specified rating
  Window<BookReview> bookReviews = bookRepository.findFirst5ByBookRating(rating, offset);
  List<BookReview> bookReviewsResult = new ArrayList<>();
  do {
    // Adds each BookReview to result
    bookReviews.forEach(bookReviewsResult::add);
    // Retrieves the next batch of 5 books with the specified rating
    bookReviews = bookRepository
      .findFirst5ByBookRating(
        rating, 
        (OffsetScrollPosition) bookReviews.positionAt(bookReviews.size() - 1));
  } while (!bookReviews.isEmpty() && bookReviews.hasNext());

   return bookReviewsResult;
}
Örnek
Şöyle  yaparız.   WindowIterator + OffsetScrollPosition  kullanılır. Window.hasNext() çağrısı yapmaya gerek yok
public List<BookReview> getBooksUsingOffSetFilteringAndWindowIterator(String rating) {
  WindowIterator<BookReview> bookReviews = WindowIterator.of(position -> 
    bookRepository
      .findFirst5ByBookRating("3.5", (OffsetScrollPosition) position))
      .startingAt(ScrollPosition.offset());

  List<BookReview> bookReviewsResult = new ArrayList<>();
  bookReviews.forEachRemaining(bookReviewsResult::add);

  return bookReviewsResult;
}
Keyset-Filtering
Açıklaması şöyleWindowIterator + KeysetScrollPosition  kullanılır
Keyset filtering helps the retrieval of a subset of results using the built-in capabilities of the database aiming to reduce the computation and IO requirements for individual queries.
The database only needs to construct smaller results from the given keyset position without materializing a large full result
Örnek
Şöyle yaparız
public List<BookReview> getBooksUsingKeySetFiltering(String rating) {
  WindowIterator<BookReview> bookReviews = WindowIterator.of(position -> 
    bookRepository
      .findFirst5ByBookRating(rating, (KeysetScrollPosition) position))
      .startingAt(ScrollPosition.keyset());
    
  List<BookReview> bookReviewsResult = new ArrayList<>();
  bookReviews.forEachRemaining(bookReviewsResult::add);

  return bookReviewsResult;
}

2 Kasım 2023 Perşembe

Neural Autonomic Transport System (NATS) Kullanımı

Giriş
Açıklaması şöyle
History and Evolution
NATS was originally created by Derek Collison, who, inspired by his experiences with building messaging systems, aimed to craft a messaging solution that embodies simplicity and performance. Over time, as cloud architectures grew in popularity, NATS emerged as a popular choice, primarily because of its inherent characteristics that matched the requirements of cloud-native systems.

Maven
Şu satırı dahil ederiz
<dependency>
    <groupId>io.nats</groupId>
    <artifactId>java-nats</artifactId>
    <version>latest_version</version>
</dependency>
Docker
Şöyle yaparız
docker run -d -p 4222:4222 -p 8222:8222 --name nats-main nats:latest

Go to http://localhost:8222
Connection Sınıfı
constructor
application.properties şöyle olsun
nats.url=nats://localhost:4222
Şöyle yaparız
@Configuration
public class NatsConfig {
    
  @Value("${nats.url}")
  private String natsUrl;

  @Bean
  public Connection natsConnection() throws IOException, InterruptedException {
    Options options = new Options.Builder().server(natsUrl).build();
    return Nats.connect(options);
  }
}
publish metodu
Şöyle yaparız
@Autowired
private Connection natsConnection;

public void sendMessage(String subject, String message) {
  natsConnection.publish(subject, message.getBytes(StandardCharsets.UTF_8));
}
subscribe metodu
Şöyle yaparız
@Autowired
private Connection natsConnection;

public void subscribeToSubject(String subject) {
  natsConnection.subscribe(subject, msg -> {
    String receivedMessage = new String(msg.getData(), StandardCharsets.UTF_8);
    // Handle and process the received message
  });
}


1 Kasım 2023 Çarşamba

SpringWebFlux Flux NettyServerCustomizer Arayüzü

Giriş
Şu satırı dahil ederiz 
import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
idleTimeout metodu
Açıklaması şöyle. Eğer bu değer verilmezse bağlantı hiç kapanmayabilir.
Reactor-Netty doesn’t have a default idle connection timeout.
Örnek
Şöyle yaparız
@Configuration
public class NettyServerCustomizerConfig {

  @Bean
  public NettyServerCustomizer nettyServerCustomizer() {
    return httpServer -> httpServer.idleTimeout(Duration.ofMillis(1));
  }
}
aynı şeyi şöyle yaparız
server:
  netty:
    idle-timeout: 1000 # 1 second of idle-timeout


31 Ekim 2023 Salı

Protocol Buffers Kullanımı

Maven
Şu satırı dahil ederiz
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-protobuf</artifactId>
</dependency>
2. src/main/proto/ dizinine proto dosyasını tanımlarız
Örnek
Şöyle yaparız
syntax = "proto3";
package example;

message Product {
    string id = 1;
    string name = 2;
    double price = 3;
}
3. plugin tanımlarız
Örnek
Şöyle yaparız
<!-- pom.xml configuration for protobuf-maven-plugin -->
<build>
    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
            ...
        </plugin>
    </plugins>
</build>
4. Rest Controller tanımlarız
Örnek
Şöyle yaparız
@RestController
public class ProductController {

    @PostMapping("/product")
    public Product addProduct(@RequestBody Product product) {
        // Business logic here
        return product;
    }
}
Açıklaması şöyle
When a request is received, Spring will automatically handle the deserialization from the Protocol Buffers format to a Product object. Similarly, the response will be automatically serialized to the Protocol Buffers format.