29 Eylül 2022 Perşembe

Pulsar API

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>org.apache.pulsar</groupId>
  <artifactId>pulsar-client</artifactId>
  <version>2.10.1</version>
</dependency>
Consumer API
Consumer Arayüzü
Örnek
Şöyle yaparız
import org.apache.pulsar.client.api.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class PulsarConfig {
  private final ConsumerProperties consumerProperties;

  public PulsarConfig(ConsumerProperties consumerProperties) {
    this.consumerProperties = consumerProperties;
  }

  @SneakyThrows(PulsarClientException.class)
  @Bean
  public PulsarClient buildClient() {
    return PulsarClient.builder()
      .serviceUrl(consumerProperties.getUrl())
      .build();
  }

  @Bean
  public Consumer<Notification> consumer(PulsarClient client,
                                         MessageListener<Notification> listener) 
    throws PulsarClientException {
      return client.newConsumer(Schema.AVRO(Notification.class))
        .topic(consumerProperties.getTopic())
        .subscriptionName(consumerProperties.getSubscriptionName())
        .messageListener(listener)
        .subscribe();
  }
}
Message Arayüzü
Örnek
Şöyle yaparız
import org.apache.pulsar.client.api.Consumer;
import org.apache.pulsar.client.api.Message;
import org.apache.pulsar.client.api.MessageListener;

@Component
@Slf4j
public class NotificationMessageListener implements MessageListener<Notification> {
  @Override
  public void received(Consumer<Notification> consumer, Message<Notification> msg) {
    try {
      log.info("Topic Name: {}", msg.getTopicName());
      log.info("Message Id: {}", msg.getMessageId());
      log.info("Producer Name: {}", msg.getProducerName());
      log.info("Publish Time: {}", msg.getPublishTime());

      //log.info("Message received: {}", new String(msg.getData()));
      Notification notification = msg.getValue();
      log.info("Message received => Username: {}, Email: {}, 
               Subject: {}, content: {}", 
               notification.getRecipientName(),
               notification.getRecipientEmail(), 
               notification.getSubject(), 
              StringUtils.abbreviate(notification.getContent(), 100));

      log.info("###########################################################");
      consumer.acknowledge(msg);
    } catch (Exception e) {
      consumer.negativeAcknowledge(msg);
    }
  }
}
Producer API
Producer Arayüzü
Örnek
Şöyle yaparız
import org.apache.pulsar.client.api.Producer;
import org.apache.pulsar.client.api.PulsarClient;
import org.apache.pulsar.client.api.PulsarClientException;
import org.apache.pulsar.client.api.Schema;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PulsarConfig {

  private final ProducerProperties producerProperties;

  public PulsarConfig(ProducerProperties producerProperties) {
    this.producerProperties = producerProperties;
  }

  @SneakyThrows(PulsarClientException.class)
  @Bean
  public PulsarClient buildClient() {
    return PulsarClient.builder()
      .serviceUrl(producerProperties.getUrl())
      .build();
  }

  @Bean
  public Producer<Notification> producer(PulsarClient client) 
    throws PulsarClientException {
    
    return client.newProducer(Schema.AVRO(Notification.class))
      .topic(producerProperties.getTopic())
      .producerName(producerProperties.getProducerName())
      .create();
  }
}
send metodu
Örnek
Şöyle yaparız
void sendMsgAsync(Notification notification) {
  producer.sendAsync(notification)
     .thenAccept(msgId -> 
        log.info("Notification message with ID {} successfully sent", msgId));
}




23 Eylül 2022 Cuma

Heap Dump Alma Yöntemleri

Heap Dump Nedir
Açıklaması şöyle
A heap dump is a snapshot of JVM memory. Typically it would be a large binary file with .hprof extension that is roughly the same size as the heap of the application at the moment it is taken.
Heap Dump Oluşturmak
Uygulama çökmese bile heap dump oluşturmak mümkün. Bazı çözümler anlatan bir yazı burada.

Hata Oluşmadan Elle Çalıştırılan Komutlar
1. jmap komutu kullanılabilir.
2. jcmd komutu kullanılabilir.
3. JVisualVM kullanılabilir
4. JMX kullanılabilir. Açıklaması şöyle. Bu JMX bean'i aynı zamanda kod içinde çağırmak ta mümkün.
There is a com.sun.management:type=HotSpotDiagnostic MBean. This MBean has ‘dumpHeap’ operation. Invoking this operation will capture the heap dump. ‘dumpHeap’ operation takes two input parameters:

outputFile: File path where heap dump should be written
live: When ‘true’ is passed only live objects in heap are captured
Hata Olunca Heapdump Almak
1. java komutu OutOfMemoryError seçenekleri kullanılabilir

Heap dump analyzer
Bu dosyayı bir heap dump analyzer ile incelemek gerekir. Heap dump alırken sadece  live nesneleri görmek işi kolaylaştırabilir. Açıklaması şöyle
Note: It’s quite important to pass “live” option. If this option is passed, then only live objects in the memory are written into the heap dump file. If this option is not passed, all the objects, even the ones which are ready to be garbage collected are printed in the heap dump file. It will increase the heap dump file size significantly. It will also make the analysis tedious. To troubleshoot memory problems or optimize memory, just the “live” option should suffice the need.

yCrash

Giriş
Açıklaması şöyle
We used yCrash tool to troubleshoot the problem. We let the application take traffic for 15 minutes. After that we executed ‘yCrash script’ against this application. ‘yCrash script’ captures 360-degree artifacts from the application stack, analyzes them and presents the root cause of the problem. Data that yCrash script captures includes: Garbage Collection log, thread dump, heap dump, netstat, vmstat, iostat, top, ps……

yCrash analyzed the artifacts and reported that the application was suffering from a memory leak.
-p seçeneği
process id belirtilir.
Örnek 
Şöyle yaparız
./yc -p <pid>


20 Eylül 2022 Salı

Hikari API HikariConfig Sınıfı

Giriş
Şu satırı dahil ederiz
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
HikariConfig  içinde tanımlı bazı alanlar şöyle

connectionTimeout : Uygulama Connection almak için kaç milisaniye bekler
Açıklaması şöyle
This property controls the maximum number of milliseconds that a client (that's you) will wait for a connection from the pool. If this time is exceeded without a connection becoming available, a SQLException will be thrown. Lowest acceptable connection timeout is 250 ms. Default: 30000 (30 seconds)
idleTimeout : Kullanılmayan connection kapatılır. How long an unused connection lives in the pool
Açıklaması şöyle 
This property controls the maximum amount of time that a connection is allowed to sit idle in the pool. This setting only applies when minimumIdle is defined to be less than maximumPoolSize. Idle connections will not be retired once the pool reaches minimumIdle connections. Whether a connection is retired as idle or not is subject to a maximum variation of +30 seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout. A value of 0 means that idle connections are never removed from the pool. The minimum allowed value is 10000ms (10 seconds). Default: 600000 (10 minutes)
initializationFailTimeout
Açıklama yaz

leak-detection-threshold
Açıklama yaz

maxLifetime : how long a connection will live in the pool before being closed
Açıklaması şöyle
sets the maximum lifetime for a connection in the pool, after this the connection is closed and is replaced with the new connection in the pool. This helps in preventing issues with the long-lived connections.

maximumPoolSize : Havuzun en fazla toplam Connection sayısı. Varsayılan değer 10. 
Açıklaması şöyle
This property controls the maximum size that the pool is allowed to reach, including both idle and in-use connections. Basically this value will determine the maximum number of actual connections to the database backend. A reasonable value for this is best determined by your execution environment. When the pool reaches this size, and no idle connections are available, calls to getConnection() will block for up to connectionTimeout milliseconds before timing out. Default: 10
minimumIdle - Idle Connection Sayısı
 Açıklaması şöyle 
This property controls the minimum number of idle connections that HikariCP tries to maintain in the pool. If the idle connections dip below this value and total connections in the pool are less than maximumPoolSize, HikariCP will make a best effort to add additional connections quickly and efficiently. However, for maximum performance and responsiveness to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. Default: same as maximumPoolSize
pool-name
Açıklama yaz

registerMBeans 
Açıklama yaz


Not : Eğer property ismi dataSource. ile başlıyorsa bu HikariConfig.addDataSourceProperty() çağrısı ile atanır. Yani isim ile belirtilen değer altta kullanılan DataSource nesnesi içindir


constructor - String
Örnek
Hikari sayfasındaki örnek şöyle
HikariConfig config = new HikariConfig("/some/path/hikari.properties");
HikariDataSource ds = new HikariDataSource(config);
properties dosyası şöyledir. Ancak buradaki farklı bir kullanım var. Tüm property isimleri dataSource. ile başlıyor
dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
dataSource.user=test
dataSource.password=test
dataSource.databaseName=mydb
dataSource.portNumber=5432
dataSource.serverName=localhost
constructor - Properties
Örnek
Şöyle yaparız. Burada Properties dosyadan yükleniyor
HikariDataSource ds;
try(InputStream input = Files.newInputStream(Paths.get("config/datasource.properties"))) {
  Properties prop = new Properties();
  prop.load(input);
  HikariConfig config = new HikariConfig(prop);
  ds = new HikariDataSource(config);

} catch (IOException e) {
  ...
}
properties dosyası şöyledir
jdbcUrl=jdbc:postgresql://localhost:5432/pg_dev
username=postgres
password=example
connectionTimeout=5000
maxLifetime=30000
maximumPoolSize=10
Örnek
Şöyle yaparız. Burada normalde boolean veya long olması gereken değerleri de String olarak geçebiliyoruz.
Properties props = new Properties();
props.setProperty("jdbcUrl", "jdbc:mysql://localhost:3306/mydatabase");
props.setProperty("username", "myuser");
props.setProperty("password", "mypassword");
props.setProperty("cachePrepStmts", "true");
props.setProperty("prepStmtCacheSize", "250");
props.setProperty("prepStmtCacheSqlLimit", "2048");

HikariConfig config = new HikariConfig(props);
setPoolName
Birden fazla Connection Pool varsa isim vermek iyi olabilir. Şöyle yaparız
Properties prop = new Properties();
prop.put("poolName","mypool");
setDataSource metodu
Mevcut DataSource nesnesini Hikari olarak sarmalar. Şöyle yaparız. Burada PGSimpleDataSource nesnesi PostgreSQL sürücüsüne ait bir DataSource
@Bean
public DataSource readWriteDataSource() {
  PGSimpleDataSource dataSource = new PGSimpleDataSource();
  dataSource.setURL(primaryUrl);
  dataSource.setUser(username);
  dataSource.setPassword(password);
  return connectionPoolDataSource(dataSource);
}

@Bean
public DataSource readOnlyDataSource() {
  PGSimpleDataSource dataSource = new PGSimpleDataSource();
  dataSource.setURL(replicaUrl);
  dataSource.setUser(username);
  dataSource.setPassword(password);
  return connectionPoolDataSource(dataSource);
}

protected HikariConfig hikariConfig(DataSource dataSource) {
  HikariConfig hikariConfig = new HikariConfig();
  int cpuCores = Runtime.getRuntime().availableProcessors();
  hikariConfig.setMaximumPoolSize(cpuCores * 4);
  hikariConfig.setDataSource(dataSource);

  hikariConfig.setAutoCommit(false);
  return hikariConfig;
}

protected HikariDataSource connectionPoolDataSource(DataSource dataSource) {
  HikariConfig hikariConfig = hikariConfig(dataSource);
  return new HikariDataSource(hikariConfig);
}
setHealthCheckInterval metodu
Hikari'nin health check işlemini hangi sıklıkta yapacağını belirtir
Örnek
Şöyle yaparız
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost/mydatabase");
config.setUsername("myusername");
config.setPassword("mypassword");
config.setMinimumIdle(2);
config.setMaximumPoolSize(5);
config.setConnectionTimeout(5_000);
config.setHealthCheckInterval(60_000); // Set the health check interval to 60 seconds
HikariDataSource dataSource = new HikariDataSource(config);
setMaximumPoolSize metodu
Örnek
Şöyle yaparız
HikariConfig config = new HikariConfig();
config.setJdbcUrl("...");
config.setMaximumPoolSize(10);
return new HikariDataSource(config);
setRegisterMbeans metodu
Açıklaması şöyle
In the JMX console, navigate to “MBeans” tab and select the “com.zaxxer.hikari”
Örnek
Şöyle yaparız
@Configuration
public class HikariCPConfig {
  @Bean
  public HikariDataSource dataSource(){
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:postgresql://<url>:<port>/<db_name>");
    config.setUsername("<username>");
    config.setPassword("<password>");

    config.setRegisterMbeans(true);
    return new HikariDataSource(config);
  }
}





19 Eylül 2022 Pazartesi

Optional.stream metodu

Giriş
Java 9 ile geliyor. Açıklaması şöyle.
returns a sequential Stream containing only that valueotherwise returns an empty Stream.
Örnek
Şöyle yaparız. Eğer orderId nesnesi null ise stream de boş olacağı için geri kalen kodlar çalışmaz. 0 döner
public BigDecimal getOrderPrice(Long orderId) {
  return Optional.ofNullable(orderId) //Optinal<Long>
    .stream() //Stream<Long>
    .map(orderRepository::findByOrderId) //Stream<List<OrderLine>>
    .flatMap(Collection::stream) //Stream<OrderLine>
    .map(OrderLine::getPrice) //Stream<BigDecimal>
    .reduce(BigDecimal.ZERO, BigDecimal::add); //BigDecimal
}
Örnek
Şöyle yaparız.
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
        .stream() // Stream<List<ResponseEntry>>
        .flatMap(List::stream) // Stream<ResponseEntry>
        .filter(e -> "expectedValue".equals(e.getValue())) // filter here
        .findFirst() // Optional<ResponseEntry>
        .orElse(null); // or else null

15 Eylül 2022 Perşembe

Logback logback.xml - Property Kullanımı

Giriş
Değişken <property> tag ile tanımlanır. Erişilen yerde ${...} şeklinde kullanılır

Örnek
Şöyle yaparız
<property name="MAX_FILE_SIZE" value="1MB"/>

<appender name="RollingFile"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
  ...
  <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    ...
  </rollingPolicy>
</appender>

14 Eylül 2022 Çarşamba

Logback logback.xml Console Appender Tanımlama

Giriş
class olarak "ch.qos.logback.core.ConsoleAppender" kullanmak gerekiyor. Daha sonra da bir encoder tanımlamak lazım

encoder
pattern içinde log formatı belirtilir.
layout belirtilir ama ne olduğunu tam bilmiyorum

Örnek
Şöyle yaparız.
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  ...  
</configuration>
Örnek
Şöyle yaparız.
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <layout class="com.touchcorp.touchpoint.utils.MaskingPatternLayout">
          <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </layout>
    </encoder>
  </appender>
  ...
</configuration>
Örnek - renkli çıktı
Şöyle yaparız
<?xml version="1.0" encoding="UTF-8"?>
<included>
  <conversionRule conversionWord="customHighlightingLogLevel" converterClass="com.emcrey.common.config.CustomHighlightingLogLevel" />
  <conversionRule conversionWord="customHighlightingPackageName" converterClass="com.emcrey.common.config.CustomHighlightingPackageName" />
  <conversionRule conversionWord="customHighlightingMessage" converterClass="com.emcrey.common.config.CustomHighlightingMessage" />

 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
   <pattern>%customHighlightingLogLevel(%d{yyyy-MM-dd HH:mm:ss} %-5level) %customHighlightingPackageName(%logger{40}.%M\(%line\)): %customHighlightingMessage(%msg%n)</pattern>
  </encoder>
 </appender>
 
 <appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <file>/tmp/logs/apex-fraud-broker.log</file>
  <append>true</append>
  <immediateFlush>true</immediateFlush>
  <encoder>
   <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{40}.%M\(%line\): %msg%n</pattern>
  </encoder>
 </appender>
 
 <logger name="com.emcrey" level="debug">
  <appender-ref ref="STDOUT"/>
  <appender-ref ref="FILE"/>
 </logger>
 
 <logger name="org.springframework" level="error">
  <appender-ref ref="STDOUT"/>
  <appender-ref ref="FILE"/>
 </logger>
</included>
Sınıflar şöyle
public class CustomHighlightingLogLevel 
  extends ForegroundCompositeConverterBase<ILoggingEvent> {

  @Override
  protected String getForegroundColorCode(ILoggingEvent event) {
    Level level = event.getLevel();
    switch (level.toInt()) {
      case Level.ERROR_INT:
        return ANSIConstants.RED_FG;
      case Level.WARN_INT:
        return ANSIConstants.YELLOW_FG;
      default:
        return ANSIConstants.DEFAULT_FG;
    }
  }
}

public class CustomHighlightingMessage 
  extends ForegroundCompositeConverterBase<ILoggingEvent> {

  @Override
  protected String getForegroundColorCode(ILoggingEvent event) {
    Level level = event.getLevel();
    switch (level.toInt()) {
      case Level.ERROR_INT:
        return ANSIConstants.RED_FG;
      case Level.WARN_INT:
        return ANSIConstants.YELLOW_FG;
      default:
        return ANSIConstants.DEFAULT_FG;
    }
  }
}

public class CustomHighlightingPackageName 
  extends ForegroundCompositeConverterBase<ILoggingEvent> {

  @Override
  protected String getForegroundColorCode(ILoggingEvent event) {
    Level level = event.getLevel();
    switch (level.toInt()) {
      case Level.ERROR_INT:
        return ANSIConstants.RED_FG;
      case Level.WARN_INT:
        return ANSIConstants.YELLOW_FG;
      default:
        return ANSIConstants.CYAN_FG;
    }
  }
}



12 Eylül 2022 Pazartesi

IntelliJ Idea Profiling

Giriş
Açıklaması şöyle
Sometimes, unfortunately, a breakpoint is not enough and you need to use some more serious tools. Mostly those are tools used to detect memory leaks and high CPU usage. The Profiler in IntelliJ provides a lot of the functionalities that previously we needed external tools for.

You can open the Profiler pane through the View Menu|Tool Windows|Profiler or it might already be visible somewhere in the left|bottom|right corners. If there is a Java process running it will appear there.

The interesting bits are hidden in the context menu.
Profiler ile
1. CPU and Memory Live Charts
2. Capture Memory Snapshot
3. Get Thread Dump

işlemleri yapılabilir

Ayarlar
Açıklaması şöyle
IntelliJ IDEA 2022.3 comes with some important profile improvements. You can exclude native calls:
Şeklen şöyle


CPU and Memory Live Charts
Ulaşmak için menü şöyle

Karşımıza çıkan şey için açıklama şöyle
This will show you a timeline graph of the memory and CPU usage of the app while it’s running.

It can be very useful if you want to gauge where the app is at in terms of resource usage or detect any abnormal spikes or drops in either of those.
Şeklen şöyle

Capture Memory Snapshot
Şeklen şöyle










IntelliJ Idea Breakpoint İpuçları

Giriş
IntelliJ ile debug için Shai Almog tarafından gösterilen bazı açıklamalar burada. Bazıları farklı şeyler söylese de bence temelde 3 çeşit breakpoint var
1. Line Breakpoint
2. Field Watchpoint veya Debug Field
3. Exception Breakpoint

1. Line Breakpoint
Seçenekler şöyle
Suspend
Şeklen şöyle. Eğer bu seçenek seçili değilse, Breakpoint'e gelince thread durmaz.

Conditional Breakpoints
Condition koymak istersek şöylen şöyle

Object Marking ile işaretli bir nesneye erişileceği gibi, genel koşullar da yazılabilir. Şeklen şöyle.

Suspending single thread only
Açıklaması şöyle
By default, in the pop up you’ll see the All radio button selected. However, if you choose the Thread one, you can suspend only the thread executing the line of code where the breakpoint is.

This is usually useful when you have threads, which, if suspended, might throw exceptions and die, like threads consuming from Kafka or querying a database with a timeout. Having only one thread stop might help you keep the app running and healthy while you examine the bit of code that might be problematic.
Şeklen şöyle. Sadece bu breakpoint'e gelen thread durdurulur. Diğer thread'ler çalışmaya devam eder.


Print debug statements
Açıklaması şöyle
In the default pop up, by default the Suspend checkbox is selected. If you deselect it, the window will expand revealing a whole lot more options. One of those is Evaluate and log which means that every time the debugger would normally suspend (no matter if it’s a normal breakpoint, with a condition, suspending one or all threads) it will print whatever the statement in that textbox evaluates to instead. It can either be a string literal or anything using the variables in the scope.
Çoklu thread kullanan uygulamalarda, "Suspend" kaldırılır ve "Evaluate and log" ile thread'in log basması sağlanabilir. Şeklen şöyle.


Şeklen şöyle

Eğer breakpoint tam metod ismine konulmuşsa, More yazısına tıklarsak Method entry veya Method exit seçimi yapabiliriz. Şeklen şöyle. Burada Suspend kaldırılmış ve Method exit seçili
Eğer istersek Condition yazılabilir.  Şeklen şöyle



2. Field Watchpoint veya Debug Field
Açıklaması şöyle
This isn’t a breakpoint since the execution never stops at the field. It stops in the line of code that accesses the field. You can optionally toggle this so it will stop only for write, only for read or in both cases.
Örnek
Şeklen şöyle. Burada üye alanı değiştiren yerde kod duracak


3. Exception Breakpoint
Açıklaması şöyle
The default IDE behavior of stopping on every exception is redundant and infuriating!
Açıklaması şöyle
Sometimes, though, you cannot pinpoint the exact line causing the problem. For example in multithreaded applications exceptions are being swallowed and appear as side effects somewhere else or you are not sure where the exception is thrown from due to lack of proper logging or you just need to approach the problem more generally. In those cases, you can add a breakpoint exception.

If you hit Ctrl + Shift + F8 | Cmd + Shift +F8 you’ll see this window listing all currently added breakpoints.
Örnek
Şeklen şöyle. Burada "Class filters" ile fırlatıldığı yer seçilebilir. Ayrıca "Catch Class Filters" ve "Instance Filters" önemli

Örnek
Şeklen şöyle. Burada IllegalArgumentException nereden fırlatılırsa kod orada duracak


Diğer Breakpoint Özellikleri
1. Grouping/Naming
Breakpoint'lere "Edit Description" ile açıklama verilebilir. Ayrıca breakpoint'ler gruplanabilir.

2. Tracepoints
Açıklaması şöyle
We can use a breakpoint as an ad hoc log that doesn’t suspend execution. You can just add printouts which can include expressions, etc. 

11 Eylül 2022 Pazar

DeflaterOutputStream Sınıfı

Giriş
Şu satırı dahil ederiz
import java.util.zip.DeflaterOutputStream;
constructor
Örnek - Yanlış Kullanım
Şöyle yapmak doğru değil. Çünkü Deflator sınıfı kapatılmıyor.
outputStream = new DeflaterOutputStream(outputStream, new Deflater(), BLOCK_SIZE);
Çözüm şunlardan birisini kullanmak. Burada deflator elle kapatılıyor
outputStream = new DeflaterOutputStream(outputStream, new Deflater(), BLOCK_SIZE) {
  @Override
  public void close() throws IOException {
    try {
      super.close();
    } finally {
      def.end();
    }
  }
};
veya şöyle yapmak. Burada usesDefaultDeflater değişkeni true yapılıyor. Böylece GZIPOutputStream kendisine dışarıdan verilen nesnesi kapatıyor.
public GZIPOutputStream(OutputStream out, int size, boolean syncFlush)
      throws IOException {
  super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true),
        size,
        syncFlush);
  usesDefaultDeflater = true;
  ...
}