29 Haziran 2021 Salı

Logback ConsoleAppender Sınıfı

pattern Alanı
Örnek
Şöyle yaparız.
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>%msg%n</pattern>
  </encoder>
</appender>
Örnek
Şöyle yaparız.
<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>
filter
Şöyle yaparız. Sadece error seviyesini konsola basar.
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>%date{yyyyMMdd HH:mm:ss.SSS} %highlight(%-5level) %magenta(%-30.30([%thread])) %cyan(%-35.35logger{35}) %-30(\(%file:%line\)) - %msg%n</pattern>
  </encoder>
  <!--Log only errors to console-->
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <level>ERROR</level>
  </filter>
</appender>

withJansi Alanı
Örnek
Şöyle yaparız.
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <withJansi>true</withJansi>
  <encoder>
    <pattern>...</pattern>
  </encoder>
</appender>

27 Haziran 2021 Pazar

Stream.toList metodu

Giriş
Java 16 ile geliyor. Unmodifiable bir liste döndürür. Açıklaması şöyle.
The name toList is not intention revealing by itself. Naming the method toUnmodifiableList would have made it consistent with the same method on Collectors.
Örnek 
Şöyle yaparız
List<Integer> evenNos = nos.stream()
  .filter(i -> i % 2 == 0)
  .toList(); 
  //.collect(Collectors.toList())  
System.out.println("Even numbers : "+evenNos);

25 Haziran 2021 Cuma

Logback logback.xml RollingFileAppender Tanımlama

Giriş
Appender altında şu tag'ler kullanılır
file
filter 
rollingPolicy
encoder

file Tag
filename değerini atamakta fayda var. Böylece en yeni dosyanın ismi her zaman bellidir.

rollingPolicy Tag
TimeBasedRollingPolicy, SizeAndTimeBasedRollingPolicy, FixedWindowRollingPolicy olabilir.

TimeBasedRollingPolicy Tag
Her gün yeni bir dosyaya geçilebilir. 
Şu alanlar belirtilir.

fileNamePattern
timeBasedFileNamingAndTriggeringPolicy : maxFileSize
maxHistory : Kaç tane dosya saklanacağını değil, dosyaların kaç birim süre boyunca saklanacağını belirtir.
totalSizeCap : Açıklaması şöyle
The optional totalSizeCap property controls the total size of all archive files.
Örnek
Şöyle yaparız
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  <fileNamePattern>/logs/api-%d{yyyy-MM-dd}.%i.log.json.gz
  </fileNamePattern>
  <timeBasedFileNamingAndTriggeringPolicy 
    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

    <maxFileSize>10MB</maxFileSize>
  </timeBasedFileNamingAndTriggeringPolicy>
  <maxHistory>10</maxHistory> <!-- 10 gün sakla -->

</rollingPolicy>
Açıklaması şöyle
When using a TimeBasedRollingPolicy in Logback, it seems to be fairly common that it doesn’t operate as expected. One setting in particular seems to be a bit misleading — maxHistory
Örnek
Şöyle yaparız.
<appender name="ROOT_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>${LOG_HOME}/main-error.log</file>
  <filter class="logging.LogbackCustomFilter"/>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <!-- daily rollover -->
    <fileNamePattern>${LOG_HOME}/main-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <timeBasedFileNamingAndTriggeringPolicy
      class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
       <!-- or whenever the file size reaches 1GB -->
      <maxFileSize>1GB</maxFileSize>
    </timeBasedFileNamingAndTriggeringPolicy>
    <!-- keep 7 days' worth of history -->
    <maxHistory>7</maxHistory>
  </rollingPolicy>
  <encoder>
    <pattern>%date %-5level [%thread] - [%logger] - %msg%n</pattern>
  </encoder>
</appender>
SizeAndTimeBasedRollingPolicy Tag
Her gün yeni bir dosyaya geçilebilir veya dosya büyüklüğü belli bir sınırı aşınca yeni dosyaya geçilebilir.

Örnek - totalSizeCap
İstenirse geçmişse ait belli bir gün kadar dosya ve en fazla belli bir toplam değer kadar disk alanı kullanır.  Burada önemli bir nokta var. Temizlenecek dosyalar fileNamePattern'a göre aranıyor. Yani fileNamePattern bir regex olarak kullanılıyor. Eğer fileNamePattern kendi içinde timestamp gibi bir şey kullanıyorsa temizleme gerçekleşmez. Elimizde şöyle bir xml olsun. Burada fileNamePattern içinde uzantıdan önce timestamp var. Dolayısıyla temizleme olmuyor
<configuration ...>
  <timestamp key="timestamp" datePattern="HH.mm,ss"/>
  ...
  <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>log/foo.log</file>
    <encoder>
      <pattern>%date{yyyMMdd HH:mm:ss.SSS} -%-32([%thread]) %-5level %-36logger{20}
%-30(\(%file:%line\)) - %msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <fileNamePattern>log/foo-%d{yyy-MM-dd} (run at $(timestamp}).%i.log</fileNamePatter>
      ...
    </rollingPolicy
  </appender>
...
</configuration>
Şöyle yaparız
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>mylog.txt</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <!-- rollover daily -->
    <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file is at most 100MB, keep 60 days worth of history,but at most 20GB -->
    <maxFileSize>100MB</maxFileSize>    
    <maxHistory>60</maxHistory>
    <totalSizeCap>20GB</totalSizeCap>
  </rollingPolicy>
  <encoder>
    <pattern>%msg%n</pattern>
  </encoder>
</appender>
Eğer uygulama açılırken eski dosyaları silsin istiyorsak cleanHistoryOnStart kullanılır. Şöyle yaparız.
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <!-- rollover daily -->
    <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file is at most 100MB, keep 60 days worth of history,but at most 20GB -->
    <maxFileSize>100MB</maxFileSize>    
    <maxHistory>60</maxHistory>
    <totalSizeCap>20GB</totalSizeCap>
  <cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
FixedWindowRollingPolicy Tag
Sadece belli sayıda log dosyası saklar. Saklanacak dosya sayısı minIndex ve maxIndex ile belirtilir.
Örnek
Şöyle yaparız.
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>logs/touchpoint.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    <fileNamePattern>logs/touchpoint.%i.log.zip</fileNamePattern>
    <minIndex>1</minIndex>
    <maxIndex>3</maxIndex>
  </rollingPolicy>

  <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <maxFileSize>10MB</maxFileSize>
  </triggeringPolicy>
  <encoder>
    ...
  </encoder>
</appender>

24 Haziran 2021 Perşembe

MapStruct @MapMapping Anotasyonu

Giriş
Açıklaması şöyle
The @MapMapping annotation has several methods to configure our Map transformation and the good thing is, as always, we don’t need to provide any implementation. Mapstruct will write that for us.
Örnek
Şöyle yaparız
@Mapper
public interface SourceTargetMapper {
  @Mapping(valueDateFormat = "dd.MM.yyyy")
  Map<String,String> longDateMapToStringStringMap(Map<Long,Date> source);
}

MapStruct @MappingTarget Anotasyonu

Giriş
Açıklaması şöyle. Bu durumda metodun return tipi void olur
In some cases you need mappings which don’t create a new instance of the target type but instead update an existing instance of that type. This sort of mapping can be realized by adding a parameter for the target object and marking this parameter with @MappingTarget. 
Örnek
Şöyle yaparız
@Mapper
public interface CustomerMapper {
  void updateFromCustomer(Customer customer, @MappingTarget CustomerDto dto);
}

MapStruct @Mapper Anotasyonu uses Alanı Seçeneği Yerine DTO

Giriş
@Mapper Anotasyonu uses alanını kullanarak ortak bir Mapper yazmak mümkün. Bu aslında güzel de bir çözüm. Ancak bazen bu yeni Mapper'ın ortak olmasına gerek yok. 

Örnek
Elimizde şöyle bir ilişki olsunn
User -> has -> Book
Book -> has -> Author
Bu ilişkideki her sınıf bir Java Bean. Bu sınıflara karşılık gelen DTO nesneleri tanımlayalım. Tüm hiyerarşiyi topluca çevirmek için şöyle yaparız
@Mapper
public interface MapStructMapper {
  BookSlimDto bookToBookSlimDto(Book book);

  BookDto bookToBookDto(Book book);

  AuthorDto authorToAuthorDto(Author author);

  AuthorAllDto authorToAuthorAllDto(Author author);

  List<AuthorAllDto> authorsToAuthorAllDtos(List<Author> authors);

  UserGetDto userToUserGetDto(User user);

  User userPostDtoToUser(UserPostDto userPostDto);
}
Eğer userToUserGetDto() çağrısı yaparsak, UserDTO nesnesinin, BookDTO ile de doldurulduğunu görebiliriz.

17 Haziran 2021 Perşembe

Reactor Netty TcpClient Sınıfı

connectNow metodu
Örnek
Şöyle yaparız
import reactor.netty.Connection;
import reactor.netty.tcp.TcpClient;

public class Application {

  public static void main(String[] args) {
    Connection connection =
      TcpClient.create()
        .host("example.com") 
        .port(80)            
        .connectNow();

    connection.onDispose()
      .block();
    }
}

Reactor Netty TcpServer Sınıfı

bindNow metodu
Örnek
Şöyle yaparız
import reactor.netty.DisposableServer;
import reactor.netty.tcp.TcpServer;

public class Application {

  public static void main(String[] args) {
    DisposableServer server =
      TcpServer.create()
        .host("localhost")
        .port(8080)       
        .bindNow();

    server.onDispose()
      .block();     
    }
}
handle metodu
Örnek
Şöylee yaparız
import io.netty.handler.ssl.util.SelfSignedCertificate;
import reactor.netty.tcp.TcpServer;
import reactor.netty.tcp.TcpSslContextSpec;

/**
 * A TCP server that sends back the received content.
 *
 * @author Violeta Georgieva
 */
public final class EchoServer {

  static final boolean SECURE = System.getProperty("secure") != null;
  static final int PORT = Integer.parseInt(System.getProperty("port",
SECURE ? "8443" : "8080")); static final boolean WIRETAP = System.getProperty("wiretap") != null; public static void main(String[] args) throws Exception { TcpServer server = TcpServer.create() .port(PORT) .wiretap(WIRETAP) .handle((in, out) -> out.send(in.receive().retain())); if (SECURE) { SelfSignedCertificate ssc = new SelfSignedCertificate(); server = server.secure( spec -> spec.sslContext(TcpSslContextSpec.forServer(ssc.certificate(),
ssc.privateKey()))); } server.bindNow() .onDispose() .block(); } }


14 Haziran 2021 Pazartesi

Mockito willThrow + given Kullanımı

Giriş
Mockito, Behavior Driven Development (BDD) yöntemiyle test edebilme imkanı da sunar. Bunun için org.mockito.BDDMockito kullanılır

Örnek
Şöyle yaparız
@Test
public void myTest() throws Exception {
  MyService service = spy(MyService.class);

  willThrow(new MyServiceException("abc msg",511))
    .given(service)
    .hi()
  ;

  // As pointed out by @eis, you can still use willAnswer
  // willAnswer(
  //   invocation -> { throw new MyServiceException("abc msg",511);}
  // )
  //   .given(service)
  //   .hi()
  // ;

  
  MyJsonResponse actual = service.hello();

  Assert.assertNotNull(actual);
  assertEquals(511, actual.getHttpResponse());
}

9 Haziran 2021 Çarşamba

MongoDBReactive MongoCollection Sınıfı

Giriş
Şu satırı dahil ederiz
import com.mongodb.reactivestreams.client.MongoCollection;
cosntructor metodu
Şöyle yaparız
MongoDatabase mongoDatabase = ...;
MongoCollection<Document> helloCollection = mongoDatabase.getCollection("hello");
findOneAndUpdate metodu
Şöyle yaparız
Mono<Long> incrementAndGetHelloCount(String name) {
  FindOneAndUpdateOptions options = new FindOneAndUpdateOptions()
    .upsert(true)
    .returnDocument(ReturnDocument.AFTER);

  return Mono.from(helloCollection.findOneAndUpdate(Filters.eq(name),
Updates.inc("count", 1L), options))
  .map(counter -> counter.get("count", Long.class));
}


8 Haziran 2021 Salı

Array İçin Covariant Nesne Atama - Covariance

Giriş 
Açıklaması şöyle. Derleme hatası almasak bile çalışma esnasında exception fırlatılması sıkça görülen bir olay
The main difference between arrays and generics is that arrays are covariant while generics are not. It means that Number[] is a supertype for Integer[]. And Object[] is a supertype for any array (except primitive ones). That seems logical, but it may lead to bugs at runtime.
Örnek
Elimizde şöyle bir kod olsun. Derleme hatası almasak bile farklı tipten bir nesne atamaya kalkarsak ArrayStoreException alırız.
Number[] nums = new Long[3];
nums[0] = 1L;
nums[1] = 2L;
nums[2] = 3L;
Object[] objs = nums;
objs[2] = "ArrayStoreException happens here";
Açıklaması şöyle
This code does compile but it throws an unexpected exception.
Eğer Generic kullansaydık derleme hatası alırdık
List<Number> nums = new ArrayList<>();
List<Long> longs = new ArrayList<>();
nums = longs; // compilation error
Örnek - Kalıtan Tipten Array'a Cast Etme
B tipinden nesne içeren A tipinden dizi, B tipinden diziye cast edilemez. Aslında derleniyor ancak çalışma zamanında exception fırlatır.
Örnek
Elimizde şöyle bir kod olsun.
public static class A { }
public static class B extends A { }

public static void main(String [] args) {
  A[] a = new A[100];
  for (int i = 0; i < a.length; i++) {
    a[i] = new B();
  }
  B[] b = (B[]) a;  /* Error: ClassCastException, even if all elements are of type B */
}