18 Ocak 2023 Çarşamba

Files Sınıfı İle Temporary Dosya Yaratma - NIO

Giriş
Dosya yaratma için createTempFile() kullanılır. Bu metodun da 2 tane overload edilmiş hali var

İmzaları şöyle
// Belirtilen dizinde temp dosya yaratır
Path createTempFile(Path dir,
                    String prefix,
                    String suffix,

                    FileAttribute<?>... attrs) throws IOException

// Sistem dizininde temp dosya yaratır
Path createTempFile(String prefix,
                    String suffix,
                    FileAttribute<?>... attrs) throws IOException
1. Her iki metod da Path nesnesi döner. Path nesnesi toFile() metodu ile File nesnesine çevrilebilir.

2. Eğer prefix null verilirse dosya ismi rastgele üretilen sayı ile başlar
Örneğin /tmp/4024018108441457842.log

3. Eğer suffix null verilirse varsayılan suffix ".tmp" olur
Örneğin /tmp/11485851858110293663.tmp

4. Eğer suffix "" yani boş string verilirse dosya ismine dahil edilmez. 
Örneğin tmp/hello7797826277559942279

5. Hem prefix hem de suffix null ise dosya ismi sadece rastgele üretilen sayı olur
Örneğin /tmp/14576820704460531496.tmp

1. Sistem Dizininde
Dosya ismi için prefix ve suffix alır.
Örnek
Şöyle yaparız. İşletim sisteminde ayarlı temporary dizin kullanılır ve rastgele bir isim üretilir.
// C:\Users\Anghel\AppData\Local\Temp\16106384687161465188.tmp
Path tmpNoPrefixSuffix = Files.createTempFile(null, null);
Örnek
Şöyle yaparız.İşletim sisteminde ayarlı temporary dizin kullanılır belirtilen prefix ile başlayıp belirtilen suffix ile biten bir isim üretilir.
// C:\Users\Anghel\AppData\Local\Temp\log_402507375350226.txt
String customFilePrefix = "log_";
String customFileSuffix = ".txt";
Path tmpCustomPrefixAndSuffix = Files.createTempFile(customFilePrefix, customFileSuffix);
Örnek
Şöyle yaparız. İşletim sisteminde ayarlı temporary dizin kullanılır belirtilen prefix ile başlayıp belirtilen suffix ile biten bir isim üretilir.
File file = Files.createTempFile("lambda", "ser").toFile();
Örnek - Permission
Tüm sistemlerde çalışır. Şöyle yaparız
// Create a new temporary file
Path jarPath = Files.createTempFile("myfile", ".jar"); 

// Make it accessible only by the owner
File jarFile = jarPath.toFile();
boolean success = jarFile.setReadable(true, true);
if (!success) {
  ...
}
success = jarFile.setWritable(true, true);
if (!success) {
  ...
}
success = jarFile.setExecutable(true, true);
if (!success) {
  ...
}
2. Belirtilen Dizinde

Belirtilen dizinde bir geçici dosya yaratır. Bu metod File.createTempFile() metoduna tercih edilmeli. Açıklaması şöyle
The temporary files created using File.createTempFile have very unrestricted file permissions so that (at least on my system) anybody can read it. If you are writing security-sensitive application (e.g. for encrypting data), it may be a serious issue.
Dosya ismi için prefix ve suffix alır.
Örnek
Şöyle yaparız. Belirtilen dizin kullanılır belirtilen prefix ile başlayıp belirtilen suffix ile biten bir isim üretilir.
// D:\tmp\log_13299365648984256372.txt
Path customBaseDir = FileSystems.getDefault().getPath("D:/tmp");
String customFilePrefix = "log_";
String customFileSuffix = ".txt";
Path tmpCustomLocationPrefixSuffix
  = Files.createTempFile(customBaseDir, customFilePrefix, customFileSuffix);
Örnek - Permission
Sadece Linux sistemlerde çalışır. Şöyle yaparız
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;

try {

 // Create a temporary file in a specified folder + file permission
 Path path = Paths.get("/home/mkyong/test/");

 // 777
 Set<PosixFilePermission> fp =
   PosixFilePermissions.fromString("rwxrwxrwx");

  Files.createTempFile(path, null, ".log",
                      PosixFilePermissions.asFileAttribute(fp));

  } catch (IOException e) {
   ...
}



16 Ocak 2023 Pazartesi

Domain Specific Language - DSL

Örnek
Gramer şöyle olsun. Burada ilginç olan Word1. Hem gramer Word1 ile bitebiliyor hem de optional word ile bitebiliyor.
Grammar ::= ( 
  'SINGLE-WORD' | 
  'PARAMETERISED-WORD' '('[A-Z]+')' |
  'WORD1' 'OPTIONAL-WORD'? | 
  'WORD2' ( 'WORD-CHOICE-A' | 'WORD-CHOICE-B' ) | 
  'WORD3'+ 
)
Elimizde şöyle bir DSL olsun.
// Initial interface, entry point of the DSL
// Depending on your DSL's nature, this can also be a class with static
// methods which can be static imported making your DSL even more fluent
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}
 
// Terminating interface, might also contain methods like execute();
interface End {
  void end();
}
 
// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}
 
// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}
 
// Intermediate interface returning itself on word3(), in order to allow
// for repetitions. Repetitions can be ended any time because this 
// interface extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}
Şöyle yaparız
Start start = ...
 
start.singleWord().end();
start.parameterisedWord("abc").end();
 
start.word1().end();
start.word1().optionalWord().end();
 
start.word2().wordChoiceA().end();
start.word2().wordChoiceB().end();
 
start.word3().end();
start.word3().word3().end();
start.word3().word3().word3().end();



Dropwizard Metrics

Giriş
3 tane önemli sınıf var.
MetricRegistry
Timer 
Slf4jReporter 

Maven
Şu satırı dahil ederiz.
<dependency>
  <groupId>io.dropwizard.metrics</groupId>
  <artifactId>metrics-core</artifactId>
  <version>${metrics.version}</version>
</dependency>
MetricRegistry Sınıfı
timer metodu
Örnek
Şöyle yaparız
MetricRegistry metricRegistry = new MetricRegistry();
 
Timer timer = metricRegistry.timer("connectionTimer");
 
Slf4jReporter logReporter = Slf4jReporter
    .forRegistry(metricRegistry)
    .outputTo(LOGGER)
    .build();
 
for (int i = 0; i < connectionAcquisitionCount; i++) {
    long startNanos = System.nanoTime();
     
    try (Connection connection = dataSource.getConnection()) {}
     
    timer.update(
        System.nanoTime() - startNanos,
        TimeUnit.NANOSECONDS
    );
}
 
logReporter.report();


13 Ocak 2023 Cuma

ConcurrentHashMap computeIfPresent metodu - Key Varsa Thread Safe Value Güncelleme

Giriş
Key varsa atomic olarak yeni value atama içindir. Açıklaması şöyle.
If the value for the specified key is present, attempts to compute a new mapping given the key and its current mapped value. The entire method invocation is performed atomically. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map.
1. Eğer BiFunction null dönerse entry silinir.
2. BiFunction exception fırlatamaz
3. computeIfPresent () çağrısının sonucu ya null ya da güncellenen value değeridir

Örnek
Bir seferinde iki tane thread ile şöyle bir şey yapmak gerekti. 
Birinci thread ConcurrentHashMap'i dolaşarak bayatlayan nesneleri siliyordu. İkinci thread ise gelen yeni değerleri value nesnesine ekliyordu. 

İlk önce ekleme işini computeIfPresent() içindeki BiFunction ile yapmak istedim. Ancak BiFunction exception fırlatan bir şey olmalıydı. Bu yüzden vaz geçtim ve value nesnesini ConcurrentHashMap.get() ile aldım. Birinci thread entry nesnesini silse bile, ben bir kere value nesnesini aldığım için sorun olmayacaktı

Örnek
Mevcut entry nesnesini silmek için şöyle yaparız.
map.computeIfPresent(k, (key, value) -> {
    //process the value here
    //key is k
    //value is the value to which k is mapped.

    return null; //return null to remove the entry
});

2 Ocak 2023 Pazartesi

Stream.onClose metodu

Giriş
Stream kapatılınca onClose() çağrılır.
Each mapped stream is closed after its contents have been placed into this stream.
Açıklaması şöyle.
Close handlers are run when the close() method is called on the stream, and are executed in the order they were added.
Bu metod özellikle Stream tarafından kullanılan sarmalanan ve kapatılması gereken Stream, File vs gibi şeyleri kapatmak için kullanılır

Örnek  - flatMap
Stream'i kapatan bazı metodlar var. Mesela flatMap. Şöyle yaparız. Dosya işlenince silinir.
Stream.generate(...).takeWhile(Objects::nonNull)
  .flatMap(file -> {
      Path p = file.toPath();
      return Files.lines(p, Charset.defaultCharset()).onClose(() -> ...);
    })
  .forEach(System.out::println);
Örnek - flatMap
Şöyle yaparız.
// example stream
Stream<String> original=Stream.of("bla").onClose(()->System.out.println("close action"));

// this is the trick
Stream<String> autoClosed=Stream.of(original).flatMap(Function.identity());

Executors.newCachedThreadPool metodu - Sınırsız Sayıda Thread Kullanır OOM Verebilir

Giriş
Açıklaması şöyle. Kısa süren hemen cevap verilmesi gereken işler için uygundur. Ayrıca ani artışlar gösteren (burst) iş sayısına da uyum sağlar. Şeklen şöyle. Belirtilen süre geçince thread sayısının azaldığı görülebilir

Bu Executor tipini Swing uygulamasında uzun vadeli işleri (long term) çalıştırmak için kullandım.
Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using ThreadPoolExecutor constructors.
Metodun içi şöyledir. En az 0, en çok 2 milyar küsur thread yaratır. Idle kalan thread 60 saniye içinde sonlanır. SynchronousQueue hemen doluyum cevabı verdiği için yeni bir thread yaratılmasına sebep olur. Bu ThreadPoolExecutor konfigürasyonu hemen işlenmesi gereken, I/O için bekleme yapan veya kısa süreli işler için kullanılır. Örneğin uzak bir sunucuya bir sürü sorgu göndermek gibi. Eğer yanlış kullanırsak bu kod çok fazla thread açmaya çalıştığı için sistemi kilitleyebilir!
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
Örnek
Şöyle yaparız.
ExecutorService executor = Executors.newCachedThreadPool();

1 Ocak 2023 Pazar

AWS S3 API S3AsyncClient Sınıfı - Kullanmayın

Giriş
Şu satırı dahil ederiz
import software.amazon.awssdk.services.s3.S3AsyncClient;
Maven
Şu satırı dahil ederiz
<!-- AWS SDK Java V2  -->
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.18.41</version>
</dependency>
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>netty-nio-client</artifactId>
    <version>2.18.41</version>
</dependency>
application.properties şöyle olsun
# AWS properties
aws:
  access-key: test
  secret-key: test
  region: eu-west-1
  s3-bucket-name: my-test-bucket
  multipart-min-part-size: 5242880 # 5MB
  endpoint: http://localhost:4566/
constructor
Şu satırı dahil ederiz
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
Şöyle yaparız
@RequiredArgsConstructor
@Configuration
public class AwsS3Config {
  private final AwsProperties s3ConfigProperties;

  @Bean
  public S3AsyncClient s3AsyncClient(AwsCredentialsProvider awsCredentialsProvider) {
    return S3AsyncClient.builder()
      .httpClient(sdkAsyncHttpClient())
      .region(Region.of(s3ConfigProperties.getRegion()))
      .credentialsProvider(awsCredentialsProvider)
      .endpointOverride(URI.create(s3ConfigProperties.getEndpoint()))
      .forcePathStyle(true)
      .serviceConfiguration(s3Configuration()).build();
  }

  private SdkAsyncHttpClient sdkAsyncHttpClient() {
    return NettyNioAsyncHttpClient.builder()
      .writeTimeout(Duration.ZERO)
      .maxConcurrency(64)
      .build();
  }

  private S3Configuration s3Configuration() {
    return S3Configuration.builder()
       .checksumValidationEnabled(false)
       .chunkedEncodingEnabled(true)
       .build();
  }
  @Bean
  AwsCredentialsProvider awsCredentialsProvider() {
    return () -> AwsBasicCredentials.create(s3ConfigProperties.getAccessKey(), 
      s3ConfigProperties.getSecretKey());
  }
}
deleteObject metodu
Şöyle yaparız
public Mono<Void> deleteObject(@NotNull String objectKey) {
  return Mono.just(DeleteObjectRequest.builder()
      .bucket(s3ConfigProperties.getS3BucketName())
      .key(objectKey)
      .build())
    .map(s3AsyncClient::deleteObject)
    .flatMap(Mono::fromFuture)
    .then();
  }
getObject metodu
Şöyle yaparız
public Mono<Void> deleteObject(@NotNull String objectKey) {
  return Mono.just(DeleteObjectRequest.builder()
      .bucket(s3ConfigProperties.getS3BucketName())
      .key(objectKey)
      .build())
    .map(s3AsyncClient::deleteObject)
    .flatMap(Mono::fromFuture)
    .then();
}