26 Temmuz 2019 Cuma

PowerMockito doNothing + when Kullanımı

Giriş
static void metodu mock'lamak içindir.

Örnek
Şöyle yaparız.
@RunWith(PowerMockRunner.class)
@PrepareForTest(PowerTest.ClassToUT.class)
public class PowerTest {

  public static class ClassToUT {

    public static String getEvent(String id) {
      return getEvent(id, null);
    }

    private static String getEvent(String id, String name) {
      // do something
      logEvent(id, name);
      return "s";
    }

    private static void logEvent(String id, String name) {
      throw new RuntimeException();
    }
  }

  @Test
  public void testGetEvent() throws Exception {

    PowerMockito.spy(ClassToUT.class);
    PowerMockito.doNothing().when(ClassToUT.class, "logEvent", any(), any());

      Assert.assertEquals("s", ClassToUT.getEvent("xyz"));
  }
}

24 Temmuz 2019 Çarşamba

Türkçe Locale (Yerel Ayar) Problemi

Giriş
Bir diğer ismiyle - Infamous Turkish Locale Problem - yani Türkçe Yerel Ayar Kepazeliği :)

Eğer varsayılan locale Locale.US ise büyük I harfi küçüğe çevrilince i olur.
Eğer varsayılan locale new Locale ("tr,"TR) ise büyük I harfi küçüğe çevrilince ı olur.

Problem Nerede?
Diyelim ki bir Map nesnesini İngilizce doldurduk ve daha sonra uygulamamız çalışırken Locale'imizi Türkçe yaptık. Map şöyle olsun
Key : "initial"
Value : "myvalue"

Map üzerinde arama işleminde key olarak "Initial" kelimesini arayıyor olalım. İngilizce çalışırken bu kelimeyi küçük harfe çevirirsek "initial" elde ederiz ve sorun çıkmaz.

Ancak çalışma esnasında locale Türkçe yapılırsa bu kelimeyi küçük harfe çevirirsek "ınitial" elde ederiz ve bu durumda arama işlemi başarısız olur.

Çözüm 1
Çözüm Apache Commons projesindeki CaseInsentiveMap'i kullanmak.

Çözüm 2
Şöyle yaparız. Bu çözümün eksikliği arama işleminin O(1) yerine Log(N) olması
Map<String, Object> caseInsensitiveMap = new TreeMap<String, Object>(
        String.CASE_INSENSITIVE_ORDER);




22 Temmuz 2019 Pazartesi

ToolProvider Sınıfı

getSystemJavaCompiler metodu
Açıklaması şöyle. JVM'in JDK olup olmadığını döner.
javax.tools.ToolProvider.getSystemJavaCompiler() will return null if no compiler is available, and a JavaCompiler if it is.
Örnek
Şöyle yaparız.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

18 Temmuz 2019 Perşembe

Java Debug Wire Protocol - JDWP - Remote Debug İçindir

Giriş
JDWP yani protokol aslında  Java Debugger Platform Architecture (JDPA)'ın bir parçası. Açıklaması şöyle
JDPA, developed by Sun Microsystems, is a multi-tiered architecture that allows users to connect to remote Java applications through their local IDE and perform the necessary debugging on the remote system. As can be seen from the diagram above, the JDPA consists of three main interfaces. These are the Java Virtual Machine Tool Interface (JVM TI), the Java Debug Wire Protocol (JDWP), and the Java Debug Interface (JDI).
Şeklen şöyle

Özet
1. Uygulamayı şu seçenekler ile başlatmak gerekir
agentlib
transport
server
address
Yani şöyle
"-Xdebug -agentlib:jdwp=transport=dt_socket,address=0.0.0.0:5005,server=y,suspend=n"

2. Debugger'ı da aynı bu porta bağlanacak şekilde başlatırız

1. Debugger 

1.1 Java Debug Interface - JDI
Açıklaması şöyle
The JDI can be thought of as simply a Java API that is aimed at capturing requests and relaying information to and from the debugger. This also means that the debugger interface can be writing in any language as long as it calls upon the correct set of API endpoints provided. On the opposite end is the JVM TI which is a native programming interface. It communicates with the services in the VM and can observe and control the execution of the Java applications.
1.2 Debugger İle Bağlanma
Java uygulaması remote debug seçeneği ile başlatıldıysa debugger ile bağlanmak için şöyle yaparız

Örnek - process id
Şöyle yaparız
java -jar target/CLI-0.0.6-SNAPSHOT-shaded.jar -pid=62604
Örnek - socket portu
Şöyle yaparız
java -jar target/CLI-0.0.6-SNAPSHOT-shaded.jar -attach=8000

2. Uygulama İçin Parametreler
Uygulamamamızı uzaktan debug edebilmek için şu parametreler ile çalıştırmak gerekir.
Örnek
Kubernetes ortamında şöyle yaparız
java -agentlib:jdwp=transport=dt_socket,server=y,address=9000 ApplicationName #ssh ile güvenli hale getirme ssh remoteUser@remoteHost -L 9000:127.0.0.1:9000 -N # kubernetes ortamında portu dışarı açma kubectl port-forward podname 9000:9000
Örnek
Kubernetes ortamında deployment için şöyle yaparız
apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-deployment
spec:
  selector:
    matchLabels:
      app: application
  template:
    metadata:
      labels:
         app: application
    spec:
      containers:
        - image: ghcr.io/amrutprabhu/remote-application:1.0.0-SNAPSHOT
          imagePullPolicy: Always
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
            - name: debug-port
              containerPort: 5005
              protocol: TCP
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-Xdebug -agentlib:jdwp=transport=dt_socket,address=0.0.0.0:5005, server=y,suspend=n"
Daha sonra şöyle yaparız. Böylece debugger 5005'e bağlanınca aslında pod'a bağlanacak
kubectl port-forward <your pod name> 5005:5005

Örnek
Şöyle yaparız
java -Xdebug
  -Xnoagent
  -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
  -jar spring-petclinic-2.5.0-SNAPSHOT.jar
  -Dagentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
address Alanı
Açıklaması şöyle. Eğer server=y ise sadece port numarasını belirtir.
address=8000 specifies the address at which the debug socket will listen. In this case, the JVM will listen at port 8000 for incoming connections only from the local host (starting JDK 9).
Açıklaması şöyle.
To bind the socket to addresses allowing remote connections, either prefix the port with the host name, IP address, or an asterisk (*) to bind to all available IP addresses:
-agentlib:jdwp=transport=dt_socket,server=y,address=host1:8000
or
-agentlib:jdwp=transport=dt_socket,server=y,address=*:8000
Örnek
Şöyle yaparız. Burada 5005 numaralı port dinleniyor. Debugger bu porta bağlanacak.
FROM openjdk:11
VOLUME /tmp
EXPOSE 8082
ADD /target/catalogservice-0.0.1-SNAPSHOT.jar catalogservice-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n", 
  "-jar", "catalogservice-0.0.1-SNAPSHOT.jar"]
agentlib alanı
Şöyle yaparız. Buraya hep jdwp yazılıyor
-agentlib:jdwp=transport=dt_socket,server=y,address=localhost:8000.
transport alanı
dt_socket veya dt_shmem değerlerini alabilir.

server Alanı
Açıklaması şöyle.
server=y means that the JVM will listen for a debugger to attach to it.
Normalde server alanı ile address alanı birlikte kullanılır. Eğer address alanı belirtilmezse JVM kendisi bir adres seçer. Açıklaması şöyle.
If server=y (i.e. JVM is listening for connection), we can skip the address option, which will make it use a dynamically assigned port. Since no address was specified, this allows only local connections. The chosen port will be displayed at stdout of the JVM, e.g.:

Listening for transport dt_socket at address: 12345
suspend Alanı
Açıklaması şöyle.
suspend=y means the JVM will wait for the debugger to attach before executing the main class. This is also the default value. If set to n, the JVM will immediately execute the main class, while listening for the debugger connection.

JPA Persistence Sınıfı - EntityManagerFactory Yaratır

Giriş
Şu satırı dahil ederiz.
import javax.persistence.Persistence;
Java SE ortamında Hibernate gibi ORM'leri ilklendirmek için kullanılır. src/main/resources/META-INF/persistence.xml dosyasını okur. Açıklaması şöyle. EntityManagerFactory nesnesi yaratır.
This class contains static methods used to obtain a EntityManagerFactory instance.
Unit Test
Eğer bu sınıfı Unit Test için kullanacaksak xml dosyasının yolu bu sefer şöyle
src/test/resources/META-INF/persistence.xml

createEntityManagerFactory metodu - XML
Belirtilen string ile persistance-unit tag'i içindeki name alanı aynı olmalıdır.
Örnek
Şöyle yaparız.
EntityManagerFactory emf = Persistence.createEntityManagerFactory ("myJPA");
Örnek
Şöyle yaparız.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("demo");
EntityManager em = emf.createEntityManager();
createEntityManagerFactory metodu - String + Map
Eğer veritabanı bağlantısı ile ilgili tüm ayarlar XML içinde belirtmez istemezsek bu metodu kullanıırz.

Örnek
Şöyle yaparız
public class JpaEntityManager {

  private EntityManagerFactory emFactory;

  private final String PERSISTENCE_UNIT_NAME = "JPATest";

  private final Map<String, String> properties=new HashMap<String, String>();

  // This Method Is Used To Retrieve The 'EntityManager' Object

  public EntityManager getEntityManager() {

    String url = String.format("%s/%s", DBConstants.DB_HOST, DBConstants.DB_NAME);

    properties.put("javax.persistence.jdbc.driver",DBConstants.DB_DRIVER);

    properties.put("javax.persistence.jdbc.url", url);

    properties.put("javax.persistence.jdbc.user", DBConstants.DB_USER);

    properties.put("javax.persistence.jdbc.password",DBConstants.DB_PASSWORD);

    properties.put("useSSL", DBConstants.DB_USE_SSL);

    properties.put("requireSSL", DBConstants.DB_REQUIRED_SSL);

    properties.put("serverTimezone",DBConstants.DB_SERVER_TIMEZONE);

    emFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME,properties);

    return emFactory.createEntityManager();

 }

}
Örnek
Şöyle yaparız.
EntityManagerFactory getEntityManagerFactory() {
  return Persistence.createEntityManagerFactory( getPersistenceUnitName(),
    getProperties() );
}

Map getProperties() {
  Map result = new HashMap();

  // Read the properties from a file instead of hard-coding it here.
  // Or pass the password in from the command-line.
  result.put( "javax.persistence.jdbc.password", "PASSWORD" );

  return result;
}
generateScheme metodu
Açıklaması şöyle
Create database schemas and/or tables and/or create DDL scripts as determined by the supplied properties.
Diğer Notlar
Bazı işlemler Persitence Context'i pas geçer. Açıklaması şöyle.

4.10 Bulk Update and Delete Operations

...
Bulk update maps directly to a database update operation, bypassing optimistic locking checks. Portable applications must manually update the value of the version column, if desired, and/or manually validate the value of the version column.
The persistence context is not synchronized with the result of the bulk update or delete.
Caution should be used when executing bulk update or delete operations because they may result in inconsistencies between the database and the entities in the active persistence context. In general, bulk update and delete operations should only be performed within a transaction in a new persistence context or before fetching or accessing entities whose state might be affected by such operations.