29 Eylül 2023 Cuma

JEP 290 - Serialization Filtering

Giriş
Açıklaması şöyle
There are two approaches we can take when filtering specific serializable objects:

Whitelist — block everything and allow specific classes or packages in.
Blacklist — block specific problematic classes and packages.

A blacklist lets us block well-known vulnerabilities, and that might be enough. But we have no guarantee that we blocked everything. A whitelist is usually the more secure option, yet it might break your code if you missed a class that’s required in an edge case.
Örnek - Her Şeyi Engelle
Şöyle yaparız
java “-Djdk.serialFilter=!*” -jar MyJar.jar
Açıklaması şöyle
This command will block all serialization. Notice I need to use the quotes to prevent bash from expanding the star sign. The exclamation point means we wish to block and the star means we block everything.
Örnek - Bir Paketi Engelle
Şöyle yaparız
java “-Djdk.serialFilter=!mypackage.*” -jar MyJar.jar
Açıklaması şöyle
We’re blocking a specific package. 
Örnek - Bir Paket İzin Ver, Geri Kalan Her Şeyi Engelle
Şöyle yaparız
java “-Djdk.serialFilter=mypackage.*;!*” -jar MyJar.jar
ObjectInputFilter Sınıfı
Şu satırı dahil ederiz
import java.io.ObjectInputFilter;
Açıklaması şöyle
You can use the object deserialization filtering added in Java 9 (java.io.ObjectInputFilter). It lets you accept or reject centrain classes. Prefer whitelisting to blacklisting.
Örnek
Şöyle yaparız
ObjectInputFilter.Config.setSerialFilter(info -> info.depth() > 10 ? 
  Status.REJECTED : Status.UNDECIDED);
Açıklaması şöyle
This is a sample from the Oracle documentation of a simple serialization filter. Notice it can reject the serialization or leave it undecided. This is part of a filter chain where each stage in the validation process can reject the serialization or pass it on to the next stage. We can bind the filter globally as we do here or on a per-stream basis. The API is remarkably flexible and provides a lot of information about the process:


28 Eylül 2023 Perşembe

Testcontainers ImageFromDockerFile Sınıfı

Giriş
Şu satırı dahil ederiz
import org.testcontainers.images.builder.ImageFromDockerFile;
withDockerfile metodu
Örnek
Şöyle yaparız
ImageFromDockerfile image = new ImageFromDockerfile()
  .withDockerfile(Paths.get("Dockerfile.txt")); 

GenericContainer container = new GenericContainer(image)
  .withEnv(...)
  .withExposedPorts(...);
Dockerfile.txt şöyledir
// Dockerfile

FROM opensearchproject/opensearch:1.1.0

# Install a plugin
RUN /usr/share/opensearch/bin/opensearch-plugin install analysis-phonetic
withDockerfileFromBuilder metodu
Şö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);



25 Eylül 2023 Pazartesi

Byte Buddy AgentBuilder Sınıfı

Giriş
Gnellikle şu şekilde kullanılır
AgentBuilder.Default()
.type(...)
.transform(...)
.installOn(...)
Çalıştırılacak kod pre-main olarak düşünülür. 

Örnek
Şöyle yaparız. Burada @Repeat olarak işaretli metod 3 kere çağrılıyor
public class Repeater {

  public static void premain(String arguments, Instrumentation instrumentation) {      //1
    var withRepeatAnnotation = 
      isAnnotatedWith(named("ch.frankel.blog.instrumentation.Repeat")); 	       //2
    new AgentBuilder.Default()                                                         //3
      .type(declaresMethod(withRepeatAnnotation))                                      //4
      .transform((builder, typeDescription, classLoader, module, domain) -> builder    //5
        .method(withRepeatAnnotation)                                                  //6
        .intercept(                                                                    //7
           SuperMethodCall.INSTANCE                                                    //8
            .andThen(SuperMethodCall.INSTANCE)
            .andThen(SuperMethodCall.INSTANCE))
      ).installOn(instrumentation);                                                    //3
  }
}
Açıklaması şöyle
1. Required signature; it’s similar to the main method, with the added Instrumentation argument
2. Match that is annotated with the @Repeat annotation. The DSL reads fluently even if you don't know it (I don't).
3. Byte Buddy provides a builder to create the Java agent
4. Match all types that declare a method with the @Repeat annotation
5. Transform the class accordingly
6. Transform methods annotated with @Repeat
7. Replace the original implementation with the following
8. Call the original implementation three times
Sonra şöyle yaparız. Burada bir fat jar oluşturuluyor
<plugin>
  <artifactId>maven-assembly-plugin</artifactId>                       
  <configuration>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>  
    </descriptorRefs>
    <archive>
      <manifestEntries>
        <Premain-Class>ch.frankel.blog.instrumentation.Repeater</Premain-Class>
      </manifestEntries>
    </archive>
  </configuration>
  <executions>
    <execution>
        <goals>
          <goal>single</goal>
        </goals>
      <phase>package</phase>
    </execution>
  </executions>
</plugin>
ve şöyle yaparız
// create the agent
mvn install

// Run the app with the agent:
java 
  -javaagent:/~/.m2/repository/.../agent-1.0-SNAPSHOT-jar-with-dependencies.jar \ #1
  -cp ./target/classes                                                            #2
  ch.frankel.blog.instrumentation.Main                                          

Örnek
Elimizde şöyle bir kod olsun. Bu instrumentation için kullanılacak kod. Bu koddaki premain metodu jvm'den önce çalışır. TimeFooter sınıfı Advice içindir. transform() metoduyla Advice metodlara takılır
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

public class SimpleInstru {
  public static void premain(String argument,Instrumentation instrumentation) {

    System.out.println("Agent started at " + System.currentTimeMillis());

    Advice advice = Advice.to(TimeFooter.class);
    new AgentBuilder.Default()
      .type(ElementMatchers.nameContains("ApplicationBeingTransformed"))
      .transform((DynamicType.Builder<?> builder,
                TypeDescription type,
                  ClassLoader loader,
                  JavaModule module) -> {
                    return builder.visit(advice.on(ElementMatchers.isMethod()));
      }).installOn(instrumentation);
    }

    public static class TimeFooter {

      @Advice.OnMethodExit(onThrowable = Throwable.class)
      public static void exit(@Advice.Origin String methodName) {
        System.out.println("Now exiting: \n"+methodName);
      }
  }
}
Bu kodu agent olarak kullanılabilecek bir jar haline getirelim
javac -cp byte-buddy-1.10.22.jar;. SimpleInstru.java
jar cfm simpleInstrumentation.jar instru.mf
SimpleInstru.class SimpleInstru\$TimeFooter.class
Agent olması için instru.mf dosyası şöyle olmalı
Premain-Class: SimpleInstru
Class-Path: byte-buddy-1.10.22.jar
Daha sonra kendi kodumuzu çalıştırmak için şöyle yaparız
java -javaagent:simpleInstrumentation.jar -jar simpleApplication.jar

jcmd komutu VM Seçenekleri

VM.classloader_stats
Örnek
Şöyle yaparız
jcmd <PID> VM.classloader_stats jcmd <PID> VM.class_hierarchy
VM.flags
Açıklaması şöyle
To check what our current startup flags
VM.native_memory
Açıklaması şöyle
This command is used to interact with and query Native Memory Tracking (NMT) data in a Java Virtual Machine (JVM) process. Native Memory Tracking is a feature in Java that allows you to monitor and track native memory usage by the JVM. Native memory includes memory allocated outside the Java heap, such as memory used by native libraries, direct byte buffers, and memory mapped files.

Şöyle yaparız
jcmd <pid> VM.native_memory
VM.native_memory_detail.diff
Örnek
Şöyle yaparız
// Define a baseline for NMT // The reason behind this is to define a reference where we can compare // the memory increment with time. jcmd <PID> VM.native_memory baseline scale=MB // Capture the memory details jcmd <PID> VM.native_memory detail scale=MB > native_memory_detail // Capture the diff jcmd <PID> VM.native_memory detail.diff scale=MB > native_memory_diff
baseline şeklen şöyle
Bu çıktıdaki kelimelerin anlamı şöyle
reserved : İşletim sisteminden alınan miktar
committed : Şu anda kullanılan miktar

diff şeklen şöyle. Burada +251MB gibi bir rakam ile her gruptaki değişiklik gösteriliyor. Baseline'a göre toplamda 842MB daha fazla kullanılıyor.

Örnek
Önce bir baseline almak gerekir. Şöyle yaparız
jcmd <pid> VM.native_memory baseline
Daha sonra biraz daha bekleriz ve şöyle yaparız
jcmd <pid> VM.native_memory detail.diff.

20 Eylül 2023 Çarşamba

ThreadPoolExecutor Sınıfı ve PriorityBlockingQueue

Örnek
Şöyle yaparız
class PrioritySingleThreadPoolExecutor extends ThreadPoolExecutor{
  public CustomSingleThreadPoolExecutor() {
    super(1, 1, 1000, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
  }

  @Override
  protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new CustomFutureTask<>(runnable);
  }
}

// A comparable FutureTask
class CustomFutureTask<T> extends FutureTask<T> implements Comparable<CustomFutureTask<T>>{

  private final Task task;
  public CustomFutureTask(Runnable task) {
    super(task, null);
    this.task = (Task) task;
  }

  @Override
  public int compareTo(CustomFutureTask that) {
    return Integer.compare(this.task.getPriority(), that.task.getPriority());
  }
}
- Burada kuyruk olarak PriorityBlockingQueue kullandığımız için kuyruğa eklenen nesnelerin Comparable arayüzünü gerçekleştirmesi ve FutureTask olması gerekir. Bunu sağlayan CustomFutureTask sınıfımız. 
- CustomFutureTask sınıfını da ThreadPoolExecutor#newTaskFor() metodunu override ederek yaratıyoruz

15 Eylül 2023 Cuma

JDBC DatabaseMetaData.getTables metodu - H2

Örnek - Kendi Schemam'daki Tüm Tablolar
Şöyle yaparız. Burada catalog ve schema ismi connection nesnesine göre veriliyor. Tablo ismi belirtilmiyor ama Types olarak TABLE belirtilmiş
try(ResultSet rs = conn.getMetaData().getTables(conn.getCatalog(), null, null, new String[] {"TABLE" , "VIEW"})) { ... }
INFORMATION_SCHEMA ve benim yarattığım tüm schemalar altındaki tüm tabloları bulur

Generational Z Garbage Collector

Giriş
Açıklaması şöyle. Yani ZGC çok büyük heap'leri destekliyor.
ZGC now supports Terabyte-size Java Heaps with permanent sub-millisecond pauses. No important caveats... it may use say 5-10% more memory or 5-10% slower allocation speed, but no more stop-the-world GC pauses and no more heap size limits. 
Açıklaması şöyle
Generational ZGC
The generational hypothesis is the observation that younger objects are much more likely to “die young” than older objects, at least in most cases. That’s why handling objects differently based on their age can be beneficial. Improving the collection of younger objects requires fewer resources and yields more memory.

Even without handling generations, ZGC is quite an improvement in GC pause times, reclaiming memory faster than concurrent threads consume them, at least if enough resources are available. However, all objects are stored regardless of their age, and all of them have to be checked when the GC runs.

With Java 21, the ZGC splits the heap into two logical generations: one for recently allocated objects and another for long-lived objects. The GC can focus on collecting younger and more promising objects more often without increasing pause time, keeping them under 1 millisecond.

The generational ZGC should give us

- Lower risks of allocation stalls
- Lower required heap overhead
- Lower GC CPU impact
All these benefits should come without a significant decrease in throughput compared to non-generational ZGC. Also, there shouldn’t be a need to configure the size of the generations, threads used, or the age limit ourselves.
Örnek
Şöyle yaparız
# Enable ZGC (defaults to non-generational)
$ java -XX:+UseZGC

# Use Generational ZGC
$ java -XX:+UseZGC -XX:+ZGenerational

# Do not use Generational ZGC
$ java -XX:+UseZGC -XX:-ZGenerational



14 Eylül 2023 Perşembe

com.sun.tools.attach.VirtualMachine Sınıfı

loadAgent metodu
Dynamic Agent Loading için kullanılır

Örnek
Şöyle yaparız
import java.lang.management.ManagementFactory;
import com.sun.tools.attach.VirtualMachine;

public class DynamicAgentLoader {

  public static void main(String... args) {

    int pidOfOtherJVM = ...;
    File agentJar = ...;

    try {
      VirtualMachine vm = VirtualMachine.attach(pidOfOtherJVM);
      vm.loadAgent(agentJar.toAbsolutePath);

      // ... do your work

      vm.detach();
    } catch (Exception e) {
      // ...
    }
  }
}

12 Eylül 2023 Salı

HttpRequest.Builder Sınıfı - Post - HttpClient API

POST metodu
1. header("Content-Type", "application/json") yapmak gerekir. 
2. POST() çağrısına JSON string geçilir

Örnek
Şöyle yaparız
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/data"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("{\"key\": \"value\"}"))
    .build();
Örnek
Şöyle yaparız
HttpRequest requestBody = HttpRequest.newBuilder()
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString("{\"name\": \"morpheus\",
    \"job\": \"leader\"}"))
  .uri(URI.create("https://reqres.in/api/users"))
  .build();
Örnek
Şöyle yaparız
HttpResponse<String> postData(String jsonStr, String endpoint, String accessToken) throws Exception { HttpClient httpClient = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder() .header("Content-Type", "application/json") .header("Authorization", "Bearer " + accessToken) .uri(URI.create("https://...")) .POST(HttpRequest.BodyPublishers.ofString(jsonStr)) .build(); ... }
timeout metodu
Şöyle yaparız. 
HttpRequest httpRequest = HttpRequest.newBuilder()
  .uri(URI.create(basePhoneHomeUrl))
  .timeout(Duration.ofMillis(TIMEOUT))
  .POST(HttpRequest.BodyPublishers.ofString(requestBody, StandardCharsets.UTF_8))
  .build();

11 Eylül 2023 Pazartesi

SnakeYaml Engine

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>org.snakeyaml</groupId>
  <artifactId>snakeyaml-engine</artifactId>
  <version>2.7</version>
</dependency>
Load  Sınıfı
loadFromString metodu
Örnek
Şöyle yaparız
import org.snakeyaml.engine.v2.api.Load;
import org.snakeyaml.engine.v2.api.LoadSettings;
import org.snakeyaml.engine.v2.api.LoadSettingsBuilder;
import org.snakeyaml.engine.v2.nodes.Node;
import org.snakeyaml.engine.v2.parser.ParserImpl;

public static void main(String[] args) {
  // Create a YAML string to parse
  String yamlString = "name: John\nage: 30";

  // Create a LoadSettings instance with your desired settings (optional)
  LoadSettings settings = new LoadSettingsBuilder().build();

  // Create a Load instance
  Load load = new Load(settings);

  / Parse the YAML string
  Node node = load.loadFromString(yamlString);

  // You can work with the parsed YAML data here
  System.out.println(node);
 
}
Dump Sınıfı
dumpToString metodu
Örnek
Şöyle yaparız
import org.snakeyaml.engine.v2.api.Dump;
import org.snakeyaml.engine.v2.api.DumpSettings;
import org.snakeyaml.engine.v2.api.DumpSettingsBuilder;
import org.snakeyaml.engine.v2.nodes.Node;


public static void main(String[] args) {
  // Create your YAML data as a SnakeYAML Node
  Node yamlData = ...; // Create your Node here

  // Create a DumpSettings instance with your desired settings (optional)
  DumpSettings settings = new DumpSettingsBuilder().build();

  // Create a Dump instance
  Dump dump = new Dump(settings);

  // Generate YAML from the Node
  String generatedYAML = dump.dumpToString(yamlData);

  // You can work with the generated YAML string here
  System.out.println(generatedYAML);
}

8 Eylül 2023 Cuma

HttpClient.Builder Sınıfı - HttpClient API

connectTimeout metodu
 Açıklaması şöyle
The Java 11 HTTP Client API allows developers to configure timeouts for their HTTP requests. This is particularly useful when dealing with scenarios where a response is expected within a certain time frame. By setting appropriate timeout values, applications can avoid waiting indefinitely for a response and gracefully handle timeout scenarios.
Örnek
Şöyle yaparız
HttpClient client = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(10))
    .build();
Örnek
Şöyle yaparız
HttpClient httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .connectTimeout(Duration.ofSeconds(20)) .build(); HttpRequest httpRequest = HttpRequest.newBuilder() .GET() .uri(URI.create("https://...")) .build(); HttpResponse httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); System.out.println(httpResponse.body());
followRedirects metodu
Açıklaması şöyle
The Java 11 HTTP Client API simplifies the handling of redirects in HTTP requests. By default, the API automatically follows redirects, allowing developers to easily handle scenarios where the requested resource has been moved or requires authentication. The API also provides options to control the behavior of redirects, such as maximum follow limit and redirect policy customization.
Örnek
Redirect.NORMAL şu anlama gelir
Always redirect, except from HTTPS URLs to HTTP URLs.
Şöyle yaparız
HttpClient client = HttpClient.newBuilder()
    .followRedirects(Redirect.NORMAL)
    .build();
proxy metodu
Açıklaması şöyle
The Java 11 HTTP Client API supports working with proxies, enabling applications to route their HTTP requests through a proxy server. Developers can configure the API to use a specific proxy server, provide authentication credentials if required, and handle scenarios where multiple proxy servers are available.
Örnek
Şöyle yaparız
import java.net.ProxySelector;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
 
HttpClient HTTP_CLIENT = HttpClient.newBuilder()
   // default
  .version(HttpClient.Version.HTTP_2)
  // Always redirect, except from HTTPS URLs to HTTP URLs.
  .followRedirects(HttpClient.Redirect.NORMAL) 
  .proxy(ProxySelector.getDefault())
  .build();

Örnek
Şöyle yaparız
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080));

HttpClient client = HttpClient.newBuilder()
    .proxy(proxy)
    .build();
sslContext metodu
HttpClient.Builder Sınıfı SSL yazısına taşıdım

HttpClient.Builder Sınıfı SSL - HttpClient API

Giriş
 Açıklaması şöyle
The Java 11 HTTP Client API provides extensive support for SSL/TLS configurations, ensuring secure communication over HTTPS. Developers can customize SSL/TLS parameters, specify trust stores, and handle scenarios where client certificates are required for authentication. This allows applications to securely communicate with HTTPS-based services.
Örnek
Şöyle yaparız
HttpClient client = HttpClient.newBuilder()
    .sslContext(SSLContext.getDefault())
    .build();
Örnek - Self Signed Certificate
Sunucu için key store oluşturalım
keytool -genkeypair -keyalg RSA -keysize 2048 -alias server1 -keystore server1.keystore \
  -storepass password -validity 3650 -dname "CN=localhost" \
  -ext SAN=ip:127.0.0.1,dns:localhost
İstemci için trust store oluşturalım
#export certificate from server
keytool -exportcert -keystore server1.keystore -alias server1 -file server.crt \
  -storepass password

#import certificate to client trust store
keytool -import -file server.crt -keystore client1.truststore -storepass password \
  -storetype JKS -alias server1
Sunucuyu başlatalım
Properties serverProps = new Properties();
serverProps.setProperty("javax.net.ssl.keyStore","path to server1.keystore"));
serverProps.setProperty("javax.net.ssl.keyStorePassword", "password");
serverProps.setProperty("javax.net.ssl.trustStorePassword", "password");
serverProps.setProperty("javax.net.ssl.protocol", "TLS");

// Start the server
İstemciyi başlatalım. Elimizde şöyle bir kod olsun
public static TrustManagerFactory loadTrustManagerFactory(
  String trustStorePassword,
  String trustStore,
  String trustManagerAlgorithm,
  String trustStoreType)
throws IOException, GeneralSecurityException {
  if (trustStore == null) {
    return null;
  }
  TrustManagerFactory tmf = TrustManagerFactory.getInstance(trustManagerAlgorithm);
  KeyStore ts = KeyStore.getInstance(trustStoreType);
  char[] passPhrase = trustStorePassword == null ? 
    null :
    trustStorePassword.toCharArray();

  loadKeyStore(ts, passPhrase, trustStore);
  tmf.init(ts);
  return tmf;
}

public static void loadKeyStore(KeyStore ks, char[] passPhrase, String keyStoreFile)
throws IOException, NoSuchAlgorithmException, CertificateException {
  try (InputStream in = new FileInputStream(keyStoreFile)) {
    ks.load(in, passPhrase);
  }
}
TrustManagerFactory nesnesini yükleriz.
TrustManagerFactory trustManagerFactory = loadTrustManagerFactory(
                "password",
                " path to client1.truststore"),
                TrustManagerFactory.getDefaultAlgorithm(), 
		"JKS");
Şöyle yaparız
// Create SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] clientTrustManagers = trustManagerFactory.getTrustManagers();
sslContext.init(null, clientTrustManagers, new SecureRandom());

// Use SSLContext for building HttpClient
HttpClient.Builder builder = HttpClient.newBuilder()
  .version(HttpClient.Version.HTTP_1_1)
  .sslContext(sslContext)
  .connectTimeout(Duration.ofSeconds(20))

HttpClient httpClient = builder.build();
Örnek - Trust All Certificates
Elimizde şöyle bir kod olsun
// Create a trust manager that trusts all certificates.
TrustManager [] trustAllCerts = new TrustManager [] {new X509ExtendedTrustManager () {
@Override public void checkClientTrusted (X509Certificate [] chain, String authType, Socket socket) { } @Override public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) { } @Override public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) { } @Override public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) { } @Override public X509Certificate [] getAcceptedIssuers () { return null; } @Override
public void checkClientTrusted (X509Certificate [] c, String a) { } @Override
public void checkServerTrusted (X509Certificate [] c, String a) { } }};
Şöyle yaparız
public void testIgnoreSSL(String url) {
  // Create an SSL context and initialize it with the trust manager.
  SSLContext sslContext = SSLContext.getInstance("TLS");
  sslContext.init(null, trustAllCerts, new SecureRandom());

  // Create an HTTP client and configure it to use the SSL context.
  HttpClient httpClient = HttpClient.newBuilder()
    .connectTimeout(Duration.ofMillis(10000))
    .sslContext(sslContext)
    .build();

  // Create a request and configure it to use the URL.
  HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(url))
    .GET()
    .build();

  // Send the request and print the response body.
  HttpResponse<String> response = httpClient.send(request,
    HttpResponse.BodyHandlers.ofString());
  System.out.println(response.body());
}
disableHostnameVerification
Açıklaması şöyle
When you disable hostname verification, you are essentially telling the system to ignore the hostname in the server's SSL certificate when making a secure connection. This means that the client won't check whether the server's certificate matches the hostname it's trying to connect to.
Örnek - API İl Değiştirme
API ile değiştirme kodunu ilk defa burada gördüm. Daha sonra şöyle yaptım ama çalışmadı.
private HttpClient newClient() throws IOException {
HttpClient.Builder builder = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1); if (sslEnabled) { SSLContext sslContext; try { sslContext = SSLContext.getInstance(tlsProtocol); } catch (NoSuchAlgorithmException e) { throw new IOException(e); } try { sslContext.init(clientKeyManagers, clientTrustManagers, new SecureRandom()); } catch (KeyManagementException e) { throw new IOException(e); } builder.sslContext(sslContext); // Disable host name verification in the certificate SSLParameters supportedSSLParameters = sslContext.getDefaultSSLParameters(); supportedSSLParameters.setEndpointIdentificationAlgorithm(null); builder.sslParameters(supportedSSLParameters); } // configure timeout on the entire client int timeout = 20; builder.connectTimeout(Duration.ofSeconds(timeout)); return builder .build(); }
Çünkü jdk/internal/net/http/AbstractAsyncSSLConnection.java sınıfında şu kod var ve System Property değerine bakarak benim verdiğim değeri eziyor
if (!disableHostnameVerification)
  sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
Örnek - System Property
Şöyle yaparız. Burada SSL sertifikası için yapılan host name verification etkisiz kılınıyor
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());


HttpClient.Builder builder = HttpClient.newBuilder();
SSLContext sslContext = ...;
builder.sslContext(sslContext);

HttpClient client = builder.build();
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(url))
  .build();
client.send(request, HttpResponse.BodyHandlers.ofString());

6 Eylül 2023 Çarşamba

Java Microbenchmark Harness (JMH) BufferedInputStream Test

Giriş
Elimizde BufferedInputStream işlevini sağlayan 3 tane sınıf vardı. Bunlardan 
- İki tanesi - yani Apache Common IO sınıfı ve Hazelcast'in kendi sınıfı - unsynchronized çalışıyor. 
- Bir tanesiyse - yani JDK ile gelen sınıf - synchronized çalışıyor. 

Merak ettiğimiz şey single threaded ortamda synchronized sınıfın read() metodunun maliyetinin çok fark yaratıp yaratmadığıydı

Not : JMH ile ölçüm yapan GitHub projesi burada

Sınıflar şöyle
1. java.io.BufferedInputStream;
3. org.apache.commons.io.input.UnsynchronizedBufferedInputStream;
Test kodu şöyle 
1. InputStream'den 100, 1000 ve 10_000 byte büyüklüğünde farklı bellek büyüklükleri ile okuma yappıyoruz
2. Girdi olarak kullanılan dosyada 30K satır var
// Only one forked process
@Fork(value = 1)
// Only one thread
@Threads(1)
// use the same instance of this class for the whole benchmark,
// so it is OK to have some member variables
@State(Scope.Benchmark)
// calculate the average time of one call
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
// 5 iterations to warm-up, that may last 20 milliseconds each
@Warmup(iterations = 5, time = 20, timeUnit = TimeUnit.MILLISECONDS)
// 20 iterations to measure, that may last 200 milliseconds each
@Measurement(iterations = 20, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class BufferedInputStreamJmh {
  @Param({"100", "1000", "10000"})
  private int readBufferSize;

  private BufferedInputStream bufferedInputStream;
  private BufferingInputStream bufferingInputStream;
  private UnsynchronizedBufferedInputStream unsynchronizedBufferedInputStream;

  public static void main(String[] args) throws Exception {
    org.openjdk.jmh.Main.main(args);
  }
 
  private int countNewLinesManually(InputStream inputStream, int customBytesToBuffer) throws IOException {
    byte[] buff = new byte[customBytesToBuffer];
    int count = 1;
    int bytesRead;
    while ((bytesRead = inputStream.read(buff)) != -1) {
      for (int i = 0; i < bytesRead; i++) {
        if (buff[i] == '\n') {
          count++;
        }
      }
    }
    return count;
  }
...
}
@Setup ve @Teardown içinde her bir Invocation için yeni bir stream yaratıyoruz. Amaç sadece read() metodunun synchronized olup olmaması neyi değiştiriyor onu ölçmek. Bu yüzden test içine stream yaratma maliyetini dahil etmek istemedik. InputStream için 65536 byte büyüklüğünde oldukça büyük bir buffer kullanıyoruz
@Setup(Level.Invocation)
public void setup() throws IOException {
  final int streamInternalBufferSize = 1 << 16;
  bufferedInputStream = new BufferedInputStream( new FileInputStream("myfile.txt"), streamInternalBufferSize);
  bufferingInputStream = new BufferingInputStream( new FileInputStream("myfile.txt"), streamInternalBufferSize);

  unsynchronizedBufferedInputStream = new UnsynchronizedBufferedInputStream.Builder()
    .setInputStream(new FileInputStream("myfile.txt"))
    .setBufferSize(streamInternalBufferSize)
    .get();
}

@TearDown(Level.Invocation)
public void teardown() throws IOException {
  bufferedInputStream.close();
  bufferingInputStream.close();
  unsynchronizedBufferedInputStream.close();
}
Testler şöyle
@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public int useBufferingInputStream() throws IOException {
  return countNewLinesManually(bufferingInputStream, readBufferSize);
  //assertThat(numberOfLines).isEqualTo(30_000);
}

@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public int useBufferedInputStream() throws IOException {
  return countNewLinesManually(bufferedInputStream, readBufferSize);
  //assertThat(numberOfLines).isEqualTo(30_000);
}

@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public int useUnsynchronizedBufferedInputStream() throws IOException {
  return countNewLinesManually(unsynchronizedBufferedInputStream, readBufferSize);
  //assertThat(numberOfLines).isEqualTo(30_000);
}
Test sonuçları şöyle. 

- Java 11'de hem MacOS hem de Windows 11'de BufferingInputStream daha iyi sonuç verdi. 
- JDK BufferednputStream ikinci. 
UnsynchronizedBufferedInputStream sınıfı unsynchronized kod kullanmasına rağmen üçüncü sırada. Üstelik UnsynchronizedBufferedInputStream sınıfı 100 byte okumada çok kötü sonuç veriyor.
Java 11 MacOs

Benchmark                                                    (readBufferSize)  Mode  Cnt  Score   Error  Units
BufferedInputStreamJmh.useBufferedInputStream                             100  avgt   20  0.276 ± 0.003  ms/op
BufferedInputStreamJmh.useBufferedInputStream                            1000  avgt   20  0.229 ± 0.003  ms/op
BufferedInputStreamJmh.useBufferedInputStream                           10000  avgt   20  0.236 ± 0.003  ms/op
BufferedInputStreamJmh.useBufferingInputStream                            100  avgt   20  0.268 ± 0.004  ms/op
BufferedInputStreamJmh.useBufferingInputStream                           1000  avgt   20  0.226 ± 0.001  ms/op
BufferedInputStreamJmh.useBufferingInputStream                          10000  avgt   20  0.227 ± 0.002  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream               100  avgt   20  4.163 ± 0.018  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream              1000  avgt   20  0.594 ± 0.002  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream             10000  avgt   20  0.240 ± 0.001  ms/op

Java 11 Windows

Benchmark                                                    (readBufferSize)  Mode  Cnt  Score   Error  Units
BufferedInputStreamJmh.useBufferedInputStream                             100  avgt   20  0,336 ± 0,009  ms/op
BufferedInputStreamJmh.useBufferedInputStream                            1000  avgt   20  0,267 ± 0,009  ms/op
BufferedInputStreamJmh.useBufferedInputStream                           10000  avgt   20  0,264 ± 0,014  ms/op
BufferedInputStreamJmh.useBufferingInputStream                            100  avgt   20  0,283 ± 0,008  ms/op
BufferedInputStreamJmh.useBufferingInputStream                           1000  avgt   20  0,245 ± 0,006  ms/op
BufferedInputStreamJmh.useBufferingInputStream                          10000  avgt   20  0,234 ± 0,008  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream               100  avgt   20  8,785 ± 0,135  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream              1000  avgt   20  1,076 ± 0,016  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream             10000  avgt   20  0,308 ± 0,014  ms/op
Java 17'de sonuç aynı. JDK 15 ile artık biased locking artık yok. Yani kodda synchronized varsa bile artık hep etkin.  Buna rağmen BufferedInputStream etkilenmiyor
Java 17 MacOs

Benchmark                                                    (readBufferSize)  Mode  Cnt  Score   Error  Units
BufferedInputStreamJmh.useBufferedInputStream                             100  avgt   20  0.277 ± 0.001  ms/op
BufferedInputStreamJmh.useBufferedInputStream                            1000  avgt   20  0.224 ± 0.001  ms/op
BufferedInputStreamJmh.useBufferedInputStream                           10000  avgt   20  0.231 ± 0.004  ms/op
BufferedInputStreamJmh.useBufferingInputStream                            100  avgt   20  0.268 ± 0.002  ms/op
BufferedInputStreamJmh.useBufferingInputStream                           1000  avgt   20  0.216 ± 0.001  ms/op
BufferedInputStreamJmh.useBufferingInputStream                          10000  avgt   20  0.223 ± 0.003  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream               100  avgt   20  3.582 ± 0.008  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream              1000  avgt   20  0.536 ± 0.005  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream             10000  avgt   20  0.231 ± 0.003  ms/op

Java 17 Windows

Benchmark                                                    (readBufferSize)  Mode  Cnt  Score   Error  Units
BufferedInputStreamJmh.useBufferedInputStream                             100  avgt   20  0,422 ± 0,012  ms/op
BufferedInputStreamJmh.useBufferedInputStream                            1000  avgt   20  0,302 ± 0,008  ms/op
BufferedInputStreamJmh.useBufferedInputStream                           10000  avgt   20  0,256 ± 0,008  ms/op
BufferedInputStreamJmh.useBufferingInputStream                            100  avgt   20  0,299 ± 0,004  ms/op
BufferedInputStreamJmh.useBufferingInputStream                           1000  avgt   20  0,273 ± 0,004  ms/op
BufferedInputStreamJmh.useBufferingInputStream                          10000  avgt   20  0,237 ± 0,005  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream               100  avgt   20  8,624 ± 0,184  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream              1000  avgt   20  1,076 ± 0,041  ms/op
BufferedInputStreamJmh.useUnsynchronizedBufferedInputStream             10000  avgt   20  0,300 ± 0,006  ms/op