30 Ağustos 2023 Çarşamba

DatagramChannel Sınıfı - NIO Multicast Gönderme ve Alma

Örnek
Şöyle yaparız. 
1. bind : Network interface belirtilmediği için tüm ağ bağdaştırıcılarını dahil eden bir port açar. Böylece multicast olmayan paketleri de alabiliriz.
2. join : Burada loopback interface üzerindeki 224.0.0.255:9999 adresine multicast join yapılıyor. Eğer istenirse interface ismi (eth0 gibi) direkt kullanılabilir. 
// Create a DatagramChannel.
DatagramChannel channel = DatagramChannel.open();

// Bind the channel to a local port.
channel.bind(new InetSocketAddress(9999));

// Join the multicast group.
InetAddress address = InetAddress.getByName("127.0.0.1");
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address);

InetAddress group = InetAddress.getByName("224.0.0.255");
channel.join(group,networkInterface);

// Create a datagram packet.
byte[] data = "Hello, world!".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, group, 9999);

// Send the datagram packet.
channel.send(packet);

// Close the channel.
channel.close();
Örnek
Şöyle yaparız
1. bind : Network interface belirtilmediği için tüm ağ bağdaştırıcılarını dahil eden bir port açar
2. join : Burada loopback interface üzerindeki 239.255.0.1:5000 adresine multicast join yapılıyor. Eğer istenirse interface ismi (eth0 gibi) direkt kullanılabilir. 
3. .setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface); bence gerekli değil
InetAddress address = InetAddress.getByName("127.0.0.1");
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address);
InetAddress group = InetAddress.getByName("239.255.0.1")

DatagramChannel channel = DatagramChannel.open(StandardProtocolFamily.INET)
    .setOption(StandardSocketOptions.SO_REUSEADDR, true)
    .bind(new InetSocketAddress(5000))
    .setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
MembershipKey key = channel.join(group, networkInterface
);

DatagramChannel Sınıfı - NIO UDP Sunucusu

constructor
Örnek
Şöyle yaparız
// Create a DatagramSocket
DatagramSocket socket = new DatagramSocket(9999);

// Receive a datagram packet
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
socket.receive(packet);

// Get the data from the packet
String data = new String(packet.getData(), 0, packet.getLength());

// Send a reply
String reply = "Hello, world!";
DatagramPacket replyPacket = new DatagramPacket(
  reply.getBytes(), 
  reply.length(), 
  packet.getAddress(), 
  packet.getPort());

socket.send(replyPacket);
bind metodu
Örnek
Şöyle yaparız
int port = 12345; // Change this to the desired port number
DatagramChannel channel = DatagramChannel.open(); channel.bind(new InetSocketAddress(port)); System.out.println("Server listening on port " + port); ByteBuffer buffer = ByteBuffer.allocate(1024); while (true) { buffer.clear(); InetSocketAddress clientAddress = (InetSocketAddress) channel.receive(buffer); buffer.flip(); // prepare for reading after data has been written into it System.out.println("Received message from " + clientAddress.getAddress() + ":" + clientAddress.getPort()); // Process the received data (here we're just echoing it back) channel.send(buffer, clientAddress);

29 Ağustos 2023 Salı

JavaMail IMAP

Örnek
Elimizde bir application.properties dosyası olsun
email.host=imap.example.com
email.port=993
email.protocol=imaps
email.username=your-email@example.com
email.password=your-email-password
Bir Session nesnesi yaratırız
import javax.mail.*;
import java.util.properties;

@Configuration
public class EmailConfiguration {

  @Value("${email.host}")
  private String emailHost;

  @Value("${email.port}")
  private String emailPort;

  @Value("${email.username}")
  private String emailUsername;

  @Value("${email.password}")
  private String emailPassword;

  @Bean
  public Session mailSession() {
    Properties props = new Properties();
    props.setProperty("mail.store.protocol", "imaps");
    props.setProperty("mail.imaps.host", emailHost);
    props.setProperty("mail.imaps.port", emailPort);

    // Create a new session with the properties
    Session session = Session.getInstance(props);
    session.setDebug(true); // Enable debug mode for troubleshooting

    return session;
  }
}
Sonra kendi listener nesnemizi yaratırız
@Configuration
public class EmailConfiguration {
  
  @Bean
  public EmailListener emailListener() {
    return new EmailListener(mailSession(), emailUsername, emailPassword);
  }
}
Listener şöyledir
import javax.mail.*;
import javax.mail.event.MessageCountAdapter;
import javax.mail.event.MessageCountEvent;

import com.sun.mail.imap.IMAPFolder;

public class EmailListener extends MessageCountAdapter {
  private Session session;
  private String username;
  private String password;

  public EmailListener(Session session, String username, String password) {
    this.session = session;
    this.username = username;
    this.password = password;
  }
  ...
}
startListening() metodu şöyledir
import com.sun.mail.imap.IMAPFolder;

public class EmailListener extends MessageCountAdapter {
  ...
  public void startListening() throws MessagingException, InterruptedException,
    IOException {
    Store store = session.getStore("imaps");
    store.connect(username, password);

    IMAPFolder inbox = (IMAPFolder)store.getFolder("INBOX");
    inbox.open(Folder.READ_WRITE);

    // Create a new thread to keep the connection alive
    Thread keepAliveThread = new Thread(new KeepAliveRunnable(inbox),
      "IdleConnectionKeepAlive");
    keepAliveThread.start();

    inbox.addMessageCountListener(new MessageCountAdapter() {
      @Override
      public void messagesAdded(MessageCountEvent event) {
        // Process the newly added messages
        Message[] messages = event.getMessages();
        for (Message message : messages) {
          try {
            // Implement your email processing logic here
            System.out.println("New email received: " + message.getSubject());
          } catch (MessagingException e) {
            ...
          }
        }
      }
    });

    // Start the IDLE Loop
    while (!Thread.interrupted()) {
      try {
        System.out.println("Starting IDLE");
        inbox.idle();
      } catch (MessagingException e) {
        ...
        throw new RuntimeException(e);
      }
    }
    // Interrupt and shutdown the keep-alive thread
    if (keepAliveThread.isAlive()) {
      keepAliveThread.interrupt();
    }
  }
}
KeepAliveRunnable şöyledir
import com.sun.mail.imap.IMAPFolder;
import javax.mail.MessagingException;

public class KeepAliveRunnable implements Runnable {
  private static final long KEEP_ALIVE_FREQ = 300000; // 5 minutes
  private IMAPFolder folder;
  public KeepAliveRunnable(IMAPFolder folder) {
    this.folder = folder;
  }

  @Override
  public void run() {
    while (!Thread.interrupted()) {
      try {
        Thread.sleep(KEEP_ALIVE_FREQ);

        // Perform a NOOP to keep the connection alive
        System.out.println("Performing a NOOP to keep the connection alive");
        folder.doCommand(protocol -> {
          protocol.simpleCommand("NOOP", null);
          return null;
        });
      } catch (InterruptedException e) {
        // Ignore, just aborting the thread...
      } catch (MessagingException e) {
        // Shouldn't really happen...
        System.out.println("Unexpected exception");
        e.printStackTrace();
      }
    }
  }
}
Açıklaması şöyle
We need an email server that supports the IMAP IDLE feature. IMAP IDLE allows our application to receive immediate notifications when new messages arrive in the inbox rather than relying on periodic polling.

Some servers have specific setting or timeouts that may terminate the IDLE connection after a certain period of inactivity. e.g. Gmail ~ 10 mins. So KeepAlive mechanism is necessary to receive email indefinitely.


JDBC DatabaseMetaData.getTables metodu

Giriş
Not : Bu metod çok iyi çalışmıyor. Farklı veri tabanları için farklı yöntemler kullanmak gerekir. Bir makale burada
İmzası şöyle
public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException
types için açıklama şöyle
Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
Döndürülen sütunlar şöyle
TABLE_CAT : Catalog of the table.
TABLE_SCHEM : Catalog of the schema.
TABLE_NAME : Name of the table.
TABLE_TYPE : Type of the table. (table, view, system table, global table, alias, synonym etc..)
REMARKS : Comments on the column.
TYPE_SCHEM : Schema of the table.
TYPE_NAME : Name of the type.
SELF_REFERENCING_COL_NAME : Name of the designated column of the table.
REF_GENERATION : Either SYSTEM or, USER or, DERIVED.
Ö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(), conn.getSchema(), null, new String[] {"TABLE"})) { ... }
Örnek - Veri tabanındaki Tüm Tablolar Yani Database Discovery
Şöyle yaparız. Burada catalog ve schema ve tablaneNamePattern parametreleri belirtilmediği için tüm schemalarındaki tüm tabloları buluyor. Tabloları buluyor çünkü Types olarak TABLE belirtilmiş
try(ResultSet rs = conn.getMetaData().getTables(null,null,null,new String[] {"TABLE"})) { while (rs.next()) { System.out.println(rs.getString("TABLE_SCHEM") + "." + rs.getString("TABLE_NAME")); } } // Output app96.t1 app96.t2 public.administration$account public.appmodule$uploadedfile public.audittrail$audittrailsuperclass

28 Ağustos 2023 Pazartesi

JEP 432 - Record patterns

Giriş
Hem instanceof, hem de switch expression kullanımında record destructure işlemine tabi tutulur. Yani içindeki değişkenlere bireysel olarak erişebiliriz

instanceof
Örnek
Elimizde şöyle bir kod olsun
record Point(int x, int y) { }
Şöyle yaparız
Object obj = new Point(2, 3);

if (obj instanceof Point(int x, int y)) {
    System.out.println("x: " + x + ", y: " + y);
} else {
    System.out.println("Not a point");
}
 switch expression
Şöyle yaparız
Object shape = new Circle(5);

String description = switch (shape) {
  case Point(int x, int y) -> "Point at (" + x + ", " + y + ")";
  case Circle(int radius) -> "Circle with radius " + radius;
  default -> "Unknown shape";
};

System.out.println(description);


27 Ağustos 2023 Pazar

Byte-code/Bytecode - Integer Instructions

Giriş
iX Talimatları Integer ile çalışıldığını gösterir

Operand stack
Matematiksel işlemler ve metod çağrılarında kullanılır

Örnek
Elimizde şöyle bir kod olsun
int a = 1;
a++;
int b = a * a;
int c = b - a;
System.out.println(c);
Bytecode çıktısı şöyle
0: iconst_1
1: istore_1
2: iinc          1, 1
5: iload_1
6: iload_1
7: imul
8: istore_2
9: iload_2
10: iload_1
11: isub
12: istore_3
13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_3
17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
20: return


iconst_X Instruction
Açıklaması şöyle
... the instruction iconst_1 is used to load a constant integer value of 1 onto the operand stack.
Örnek
iconst_1
Şu koda denk gelir
int a = 1;
iinc_X Instruction
Açıklaması şöyle
iinc instructs to increment an integer value. 
Örnek
iinc 1, 1
Şu koda denk gelir
a++;
Açıklaması şöyle
 The values 1, 1 specify the index of the variable to be incremented and the value by which it should be incremented, respectively.
iload_X Instruction - load an int value from a local variable #index
Bu instruction integer içindir. Farklı tipler için floaddload gibi instrunction'lar da var.

Toplama gibi bir işlem yapmak için değişkendeki değerin stack'a yazılması gerekir. iload_X bu işi yapar. Daha sonra iadd ile toplama yapılır. Ve istore_X ile sonuç değişkene yazılır. Şu kodda bu işlemleri görebiliriz
int a = ...;
int b = ...;
int c = a + b;
//Toplama işlemi şöyledir
iload_1
iload_2
iadd
istore_3
imul Instruction
Çarpma yapar

ireturn Instruction
Elimizde şöyle bir kod olsun
public static int sum(final int a, final int b) {
  return a + b;
}
Şu işlemleri görebiliriz
iload_0
iload_1
iadd
ireturn
isub Instruction
Çıkartma yapar. Açıklaması şöyle
This operation takes two integer values from the operand stack. It subtracts the integer value loaded last from the integer value loaded first. The resulting integer is loaded onto the operand stack.

istore_X Instruction
Açıklaması şöyle
Instructs to store the integer value that is currently at the top of the operand stack as a variable. It removes the top integer value from the operand stack and saves it in the local variable array at specified index
Örnek
istore_1
Şu koda denk gelir
a++;

24 Ağustos 2023 Perşembe

com.sun.source.util.Plugin Arayüzü

Giriş
Şu satırı dahil ederiz
import com.sun.source.util.Plugin;
java compiler tarafından çağrılır ve kod üretebilmeyi sağlar. java compiler javac komutu veya JavaCompiler arayüzü olabilir

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>com.sun</groupId>
  <scope>system</scope>
  <artifactId>tools</artifactId>
  <version>1.8.0</version>
  <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
init metodu
Örnek
Şöyle yaparız
public class myJavacPlugin implements Plugin { 

  @Override 
  public String getName() { 
   return "InterviewBit"; 
  }

  @Override 
  public void init(JavacTask task, String... args) { 
    Context context = ((BasicJavacTask) task).getContext(); 
    Log.instance(context) 
      .printRawLines(Log.WriterKind.NOTICE, "Hi from " + getName()); 
  } 
}
Şu dosyayı yaratırız
META-INF/services/com.sun.source.utility.Plugin
Dosyanın içi şöyledir
com.JavaTutorials.javac.myJavacPlugin
Çalıştırmak için şöyle yaparız
 javac -cp ./core-java/target/classes -Xplugin:plugin
  ./core-java/src/main/java/com/JavaTutorials/javac/sampleClass.java
Hi from plugin
TaskListener Arayüzü
Şu satırı dahil ederiz
import com.sun.source.util.JavacTask;
com.sun.source.util.JavacTask nesnesine listener takılabilir

Örnek
Şöyle yaparız
public void init(JavacTask task, String... args) {
  task.addTaskListener(new TaskListener() {
    @Override
    public void started(TaskEvent e) {
    }

    @Override
    public void finished(TaskEvent e) {
      if (e.getKind() != TaskEvent.Kind.PARSE) {
        return;
      }
      // Perform instructions
    }
  });
}
finished metodu
TaskEvent nesnesinin getKind() metodu şunları döndürebilir
PARSE - builds an Abstract Syntax Tree (AST)
ENTER - source code imports are resolved
ANALYZE - We analyze parser output for errors
GENERATE - Creating binaries for the target source file
There are two more event kinds - ANNOTATION_PROCESSING and ANNOTATION_PROCESSING_ROUND but we don’t bother about them here.

Örnek - Extract AST Data
Şöyle yaparız.  com.sun.source.util.TreeScanner kullanılıyor TreeScanner sınıfı com.sun.source.tree.TreeVisitor arayüzünü gerçekleştirir
public void finished(TaskEvent e) {
  if (e.getKind() != TaskEvent.Kind.PARSE) {
    return;
  }
  e.getCompilationUnit().accept(new TreeScanner<Void, Void>() {
    @Override
    public Void visitClass(ClassTree currentNode, Void aVoid) {
      return super.visitClass(currentNode, aVoid);
    }

    @Override
    public Void visitMethod(MethodTree currentNode, Void aVoid) {
      return super.visitMethod(currentNode, aVoid);
    }
  }, null);
}
Örnek - Modify AST Data
Şöyle yaparız. Burada amaç @Position olarak benim tanımladığım bir anotasyon için kod üretip geçilen parametre eksi bir değerse exception fırlatmak
public void finished(TaskEvent e) {
  if (e.getKind() != TaskEvent.Kind.PARSE) {
    return;
  }
  e.getCompilationUnit().accept(
    new TreeScanner<Void, Void>() {
      @Override
      public Void visitMethod(MethodTree method, Void v) {
        List<VariableTree> parametersToInstrument
          = method.getParameters().stream()
            .filter(SampleJavacPlugin.this::shouldInstrument)
            .collect(Collectors.toList());
            
        if (!parametersToInstrument.isEmpty()) {
          Collections.reverse(parametersToInstrument);
          parametersToInstrument.forEach(p -> addCheck(method, p, context));
        }
        return super.visitMethod(method, v);
      } // visitMethod
    }, null); //accept
}

private static Set<String> TARGET_TYPES = Stream.of(
  byte.class, short.class, char.class, 
  int.class, long.class, float.class, double.class)
 .map(Class::getName)
 .collect(Collectors.toSet());
 
private boolean shouldInstrument(VariableTree parameter) {
    return TARGET_TYPES.contains(parameter.getType().toString())
      && parameter.getModifiers().getAnnotations().stream()
      .anyMatch(a -> Positive.class.getSimpleName()
        .equals(a.getAnnotationType().toString()));
}
addCheck metodu AST'yi değiştiren yer. Şöyle yaparız.  Bu kodu tam olarak kopyalamadım.
private void addCheck(MethodTree myMethod, VariableTree myParameter, Context myContext) {
    JCTree.JCIf check = createCheck(myParameter, myContext);
    JCTree.JCBlock body = (JCTree.JCBlock) myMethod.getBody();
    body.stats = body.stats.prepend(check);
}

private static JCTree.JCIf createCheck(VariableTree parameter, Context context) {
    TreeMaker factory = TreeMaker.instance(context);
    Names symbolsTable = Names.instance(context);
        
    return factory.at(((JCTree) parameter).pos)
      .If(factory.Parens(
	    createIfCondition(factory, symbolsTable, parameter)),
        createIfBlock(factory, symbolsTable, parameter), 
		null)
		
}







22 Ağustos 2023 Salı

RandomGenerator Arayüzü - Java 17

Giriş
Şu satırı dahil ederiz
import java.util.random.RandomGeneratorFactory;
Açıklaması şöyle
Legacy random classes like java.util.Random, SplittableRandom, and SecureRandom were also refactored to extend the new RandomGenerator interface.
nextInt metodu
Örnek
Şöyle yaparız
RandomGenerator randomGenerator = RandomGeneratorFactory.of("Xoshiro256PlusPlus")
  .create(999);
System.out.println(randomGenerator.getClass());
for (int i = 0; i < 10; i++) {
  System.out.println(randomGenerator.nextInt(11));
}

RandomGeneratorFactory Sınıfı - Java 17

Giriş
Şu satırı dahil ederiz
import java.util.random.RandomGeneratorFactory;
Açıklaması şöyle
Legacy random classes like java.util.Random, SplittableRandom, and SecureRandom were also refactored to extend the new RandomGenerator interface.
all metodu
Tüm  java.util.random.RandomGeneratorFactory arayüzünden kalıtan sınıflar döner
Örnek
Şöyle yaparız
RandomGeneratorFactory.all()
        .map(fac -> fac.group()+ " : " + fac.name())
        .sorted()
        .forEach(System.out::println);
of metodu
Örnek
Şöyle yaparız
RandomGenerator randomGenerator = RandomGeneratorFactory.of("Xoshiro256PlusPlus")
  .create(999);
System.out.println(randomGenerator.getClass());
for (int i = 0; i < 10; i++) {
  System.out.println(randomGenerator.nextInt(11));
}

16 Ağustos 2023 Çarşamba

HttpClient.send metodu - HttpClient API

Giriş
GET veya POST için yaratılan HttpRequest nesnesini senkron olarak gönderir.

Cancel
Açıklaması şöyle. Java 20'den sonra Thread.interrupt ile yapılıyor
Since Java 16 it is possible to cancel a request by interrupting the thread that called HttpClient::send or by invoking cancel(true) on the CompletableFuture returned by HttpClient::sendAsync
Örnek
Şöyle yaparız. Burada Project Loom kullanılıyor. Aslında HttpClient  değil Virtual Thread iptal ediliyor.
import org.slf4j.LoggerFactory import java.net.URI import java.net.http.HttpResponse.BodyHandlers import java.net.http.{HttpClient, HttpRequest} import scala.sys.process._ object Run extends App { val log = LoggerFactory.getLogger(this.getClass) val client = HttpClient.newHttpClient() log.info("Starting virtual thread ...") val t = Thread.startVirtualThread(() => { log.info("Sending ...") val r = client.send( HttpRequest .newBuilder(new URI("http://localhost:8080/wait")) .GET().version(HttpClient.Version.HTTP_1_1) .build(), BodyHandlers.ofString() ) log.info(s"Received, body length: ${r.body().length}") }) Thread.sleep(1000) log.info("Interrupting ...") t.interrupt() log.info("Done.") }
send metodu
Ö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(); HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); return httpResponse; }