27 Aralık 2022 Salı

Tomcat Mimarisi

Giriş
Şeklen şöyle

Temel bileşenler şöyle
1. Server  : Tomcat'ın kendisi
2. Service : Connector ve Container içerir
3. Connector : İstemci bağlantılarını ve protokolleri içerir
4. Container : Engine'ı içerir

Açıklaması şöyle
The Server component is the outermost component of Tomcat, which is an abstraction of the Tomcat instance itself and represents Tomcat itself. A Server component can have one or more Service components.

The Service component is a set of components in Tomcat that provide services and process requests. A Service component can have multiple Connectors and a Container. Multiple Connectors indicate that it can use multiple protocols to receive user requests at the same time.

The connector is responsible for handling client connections, and it provides support for various service protocols, including BIO, NIO, AIO, etc. The value of its existence lies in shielding the complexity of multi-protocol Containers and unifying the processing standards of Containers.

The Container component is the container responsible for specific business logic processing. When the Connector component establishes a connection with the client, it forwards the request to the Engine component of the Container component for processing.

At this point, the core components of Tomcat are basically finished. In fact, there are many subdivided components in the Container component. In fact, if you are interested in the abstraction of the business, you can continue to look at it.

- The Engine component represents a runnable Servlet instance, including the core functions of the Servlet container, which can have one or more virtual hosts (Host). Its main function is to delegate the request to the appropriate virtual host for processing, that is, to match the appropriate virtual host for processing according to the configuration of the URL path.
- The Host component is responsible for running multiple applications, and it is responsible for installing these applications. Its main function is to parse the web.xml file and match it to the corresponding Context component.
- The Context component represents the specific Web application itself, and its most important function is to manage the Servlet instances inside. A Context can have one or more Servlet instances.
- A Wrapper component represents a Servlet, which is responsible for managing a Servlet, including Servlet loading, initialization, execution, and resource recovery. The wrapper is the lowest-level container.

It can be seen that Host is the abstraction of the virtual host, Context is the abstraction of the application, Wrapper is the abstraction of the Servlet, and Engine is the abstraction of the processing layer.
Container Component
Şeklen şöyle. Thread Pool'ları yönetir. Aslında acceptCount, maxConnections, maxThreads parametreler hep Connector'a tanımlanıyor. Tomcat server.xml Connector yazısına bakabilirsiniz


Engine Component
Tomcat server.xml Engine yazısına bakabilirsiniz. İçinde virtual host için tanımlamalar bulunur. Servlet'i temsil eder.

Context Component
Tomcat context.xml yazısına bakabilirsiniz. Environment variables, JNDI tanımlamaları burada yapılır





26 Aralık 2022 Pazartesi

Testcontainers GenericContainer Sınıfı

Giriş
Şu satırı dahil ederiz
import org.testcontainers.containers.GenericContainer;
constructor - dockerImageName
Örnek
Şöyle yaparız
GenericContainer redis = new GenericContainer("redis:5.0.8-alpine3.11")
  .withExposedPorts(6379);

redis.start();
// run your tests
redis.stop();
Örnek
Şöyle yaparız
@Rule
public GenericContainer<?> server = new GenericContainer(
  new ImageFromDockerFile()
    .withDockerfileFromBuilder(builder ->
      builder
        .from("alpine:3.16")
        .run("apk add --update nginx")
        .cmd("nginx", "-g", "deamon off;")
        .build()))
  .withExposedPorts(80);
getHost metodu
Açıklaması şöyle
When running with a local Docker daemon, exposed ports will usually be reachable on localhost. However, if you ever need to obtain the container address, you can do:
String ipAddress = container.getHost();
getMappedPort metodu
Açıklaması şöyle
We usually don’t want to publish to a specific port on the host, to avoid port collisions with locally running software or in between parallel test runs, so we let the Docker decide on which port to publish.

Since we don’t know the port Docker will pick, Testcontainers has additional APIs to get the actual mapped port after the container starts, so we can inject it into our tests and use it.

This can be done using the getMappedPort method, which takes the original (container) port as an argument, so for the Redis generic container example, you would do the following:

Integer mappedPort = container.getMappedPort(6379);
start metodu
Açıklaması şöyle
The start command is a blocking command, which means that it will wait until the application inside the container is ready. By default, it will wait for the container’s mapped network port to start listening. Of course, readiness can mean different things in different applications, that’s why there are other specific wait strategies that can be used with Testcontainers, but the default behavior should be already enough for most applications.
stop metodu
Açıklaması şöyle
The stop command will shut down and delete the container after the test.
Waiting for containers to start or be ready
Örnek - Http
Şöyle yaparız. Burada nginx sunucusu kullanıldığı için http isteğine cevap vermesi yeterli
GenericContainer nginxWithHttpWait = 
  new GenericContainer(DockerImageName.parse("nginx:1.9.4"))
  .withExposedPorts(80)
  .waitingFor(Wait.forHttp("/"));



Testcontainers ElasticsearchContainer Sınıfı

Gradle
Şu satırı dahil ederiz
dependencies {
...
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.testcontainers:elasticsearch:1.17.4'
testImplementation "org.testcontainers:testcontainers:1.17.5"
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.junit.jupiter:junit-jupiter'
}

dependencyManagement {
imports {
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
mavenBom "org.junit:junit-bom:5.8.1"
}
}
Örnek
Elimizde şöyle bir kod olsun
public class ElasticTestContainer extends ElasticsearchContainer {
  private static final String DOCKER_ELASTIC = 
    "docker.elastic.co/elasticsearch/elasticsearch:7.17.6";

  private static final String CLUSTER_NAME = "sample-cluster";

  private static final String ELASTIC_SEARCH = "elasticsearch";

  public ElasticTestContainer() {
    super(DOCKER_ELASTIC);
    this.addFixedExposedPort(9200, 9200);
    this.addFixedExposedPort(9300, 9300);
    this.addEnv(CLUSTER_NAME, ELASTIC_SEARCH);
  }
}
Test kodunda şöyle yaparız
// using the above test container using @container annotation
@Container
protected static ElasticsearchContainer elasticsearchContainer =
  new ElasticTestContainer();




Log4j2 JsonLayout Tanımlama

Örnek
Şöyle yaparız
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <JsonLayout complete="false"  compact="true" eventEol="true"></JsonLayout>
      <PatternLayout
        pattern="%style{%d{ISO8601}}{black} %highlight{%-5level }[%style{%t}{bright,blue}]
        %style{%C{1.}}{bright,yellow}: %msg%n%throwable" />
    </Console>
  </Appenders>
  <Loggers>
    <!-- LOG everything at INFO level -->
    <Root level="info">
      <AppenderRef ref="Console" />
     </Root>
   <Logger name="com.vb" level="trace"></Logger>
 </Loggers>
</Configuration>
Çıktısı şöyle
{
  "thread": "main",
  "level": "INFO",
  "loggerName": "com.vb.math.learnit.LearnitApplication",
  "message": "Started LearnitApplication in 2.508 seconds (JVM running for 4.586)",
  "endOfBatch": false,
  "loggerFqcn": "org.apache.commons.logging.LogAdapter$Log4jLog",
  "instant": {
    "epochSecond": 1597070439,
    "nanoOfSecond": 853000000
  },
  "threadId": 1,
  "threadPriority": 5
}

21 Aralık 2022 Çarşamba

DigestInputStream Sınıfı

Giriş
Şu satırı dahil ederiz
import java.security.DigestInputStream;
constructor
Bir tane InputStream bir tane de MessageDigest nesnesi alır. read() metodlarından birisi kullanılarak InputStream nesnesi okunur ve okunan veri MessageDigest nesnesine geçilir.

read metodu
Örnek
Şöyle yaparız. Burada InputStream okunarak SHA-256 digest alınıyor. Ancak InputStream nesnesinin okuma belleği küçük kalabilir.
public static String calculateSha256Hex(Path jarPath) 
  throws IOException, NoSuchAlgorithmException {
  MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
  try (InputStream is = Files.newInputStream(jarPath);
    DigestInputStream dis = new DigestInputStream(is, messageDigest)) {

    while (dis.read() != -1) {
      messageDigest = dis.getMessageDigest();
   }
   return bytesToHex (messageDigest.digest());
}
read metodu - byte []
Kendi buffer nesnemizi kullanmak istersek şöyle yaparız
public static String calculateSha256Hex(Path jarPath) 
throws IOException, NoSuchAlgorithmException {
  MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
  try (InputStream is= Files.newInputStream(jarPath);
    DigestInputStream dis = new DigestInputStream(is, messageDigest)) {

    // 1 MB
    final int oneMB = 1024 * 1024;
    byte[] buffer = new byte[oneMB];
    while (true) {
      int readCount = digestInputStream.read(buffer);
      if (readCount < 0) {
        break;
      }
    }
  }
  return bytesToHex (messageDigest.digest());
}
Daha Sonra MessageDigest.digest() Metodu Çağrılır
Bu MessageDigest.digest() metodunu bir kere çağırmak lazım. İkinci kere çağrılınca farklı bir sonuç döner.

Hexadecimal String döndürmek için şöyle yaparız. Burada byte değeri 0xFF ile masklanıyor çünkü normalde Java'da byte tipi signed bir tip ve Integer'a çevrilirken de sign işareti korunuyor. 

Ama bize gereken unsigned byte değeri. Diyelim ki byte değeri -128 olsun. Bunu Integer yaparsak yine -128 elde ederiz ama hex değeri 0XFFFF_FF80 olur ama unsgined değeri aslında 0x80 idi. Bu hatayı yapmamak için 0xFF ile masklanır.
public static String bytesToHex(byte[] digest) {
  StringBuilder hexString = new StringBuilder(2 * digest.length);
  for (byte b : digest) {
    String hex = Integer.toHexString(0xff & b);
    if (hex.length() == 1) {
      hexString.append('0');
    }
    hexString.append(hex);
  }
  return hexString.toString();
}



19 Aralık 2022 Pazartesi

String.formatted metodu - Java 15 İle Geliyor

Giriş
Açıklaması şöyle. String.format() yerine direkt String şablonunu kullanıyoruz.
Since Java 15, you can now format a String with a new method, formatted. This method is the same as the well know static String format method. From a readability perspective, this is the same as String.format.

This newly added method will also be easier to test as the static alternative. Technically, you can now mock it with mockito. In practice, of course, you won’t be doing that very often. But it gives you some more possibilities, which is always lovely.
İmzası şöyle
public String formatted(Object... args)
Örnek
Şöyle yaparız
String msg = "Your name is %s and Your age is %d".formatted("Nick", 18);
System.out.println(msg); // Your name is Nick and Your age is 18
Örnek
Şöyle yaparız. Burada String.format ve String.formatted farkı görülebilir
var format = "Hello %s, how are you?\nIt's %d°C today!";
var greeting = String.format(format, name, tempC);

// Java 15+
var greeting = format.formatter(name, tempC);


18 Aralık 2022 Pazar

AWS SNS API SnsClient Sınıfı

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>io.awspring.cloud</groupId>
  <artifactId>spring-cloud-aws-starter-sns</artifactId>
  <version>3.0.0-M33</version>
</dependency>
Örnek
Elimizde şöyle bir kod olsun
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;

protected AwsCredentialsProvider getAwsCredentialsProvider() {
  return AwsCredentialsProviderChain.builder()
    .addCredentialsProvider(DefaultCredentialsProvider.create())
    .build();
}
createTopic metodu
Şöyle yaparız
import software.amazon.awssdk.regions.Region;

import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.CreateTopicRequest;

protected SnsClient snsClient() throws URISyntaxException {
  return SnsClient.builder()
    .region(Region.US_EAST_1)
    .endpointOverride(new URI("http://localhost:4566"))
    .credentialsProvider(getAwsCredentialsProvider())
    .build();
}

protected String createTopic(String topicName) throws URISyntaxException {
  var createTopicRequest = CreateTopicRequest.builder()
    .name(topicName)
    .build();
  return snsClient().createTopic(createTopicRequest).topicArn();
}
subscribe metodu
Şöyle yaparız
import software.amazon.awssdk.services.sns.model.SubscribeRequest;

protected SubscribeRequest createSubscribe(String topicName, String queueName)
throws URISyntaxException {
  var topicArn = createTopic(topicName);
  var queueUrl = createQueue(queueName);

  var subscribeRequest = SubscribeRequest.builder()
    .protocol("sqs")
    .topicArn(topicArn)
    .endpoint(queueUrl)
    .build();

  snsClient().subscribe(subscribeRequest);
  return subscribeRequest;
}