4 Eylül 2019 Çarşamba

Java Singleton Örnekleri

Giriş
Singleton ile yapılamaması gereken bazı şeyler şöyle.

1. Singleton reflection ile yaratılmamalı
2. Singleton Clone yapılamamalı
3. Singleton Serializable olmamalı

1 - Static Alan Yöntemi
Açıklaması şöyle.
In eager initialization, the instance of Singleton Class is created at the time of class loading, this is the easiest method to create a singleton class but it has a drawback that instance is created even though client application might not be using it.
Bu yöntem en basit olanı
Örnek
Şöyle yaparız.
class Single {

  private final static Single sing = new Single();       
  private Single() {
  }        
  public static Single getInstance() {
    return sing;
  }
}
Singleton'dan kalıtım da olabilir. Ama ne işe yarar hayal edemiyorum. Şöyle yaparız.
class XSingleton extends Singleton {

}
2 - Static Constructor Yöntemi
Örnek
Şöyle yaparız. Yine static alan kullanılıyor, tek farkı sınıf yüklenince çalışan static constructor kullanılması.
public class SingletonConnection   {


  private static Connection connection = null ;
  static 
  {
    connection = ...;
  }
  public static Connection getConnection() throws Exception {
    return connection;
  }
}
3 - Holder Yöntemi
Holder Singleton yazısına taşıdım.

4. Static ve Syncronized Yöntemi
Şöyle yaparız. Yine static alan kullanılıyor, tek farkı staticalanın ilk getInstance() çağrısında ilklendirilmesi.
private static YourObject instance;

public static synchronized YourObject getInstance() {
    if (instance == null) {
        instance = new YourObject();
    }
    return instance;
}
5. Enum Yöntemi
Bu yöntemde enum şeklinde bir sınıf kullanılıyor. Java 1.5'ten sonra bu yöntemin kullanılması tavsiye ediliyor. Enum olduğu için sınıf otomatik olarak Serializable da oluyor. Bu yöntemin JVM'in kendi içinde kullandığı bir kilit ile bu nesneyi yarattığı dolayısıyla thread-safe olduğu belirtilmiş. Joshua Bloch şöyle yazıyor.
you get an ironclad guarantee that there can be no instances besides the declared constants. The JVM makes this guarantee, and you can depend on it.
Şöyle yaparız.
public enum UserActivity {
  INSTANCE;

  public void dostuff() {
    ...
  }
}

// use it as ...
UserActivity.INSTANCE.doStuff();

6. Double Checked Locking
Eğer multi thread ortamda lazy singleton kullanmak istersek şöyle yaparız. Nesne volatile tanımlanmalıdır! Volatile tanımlanınca "partially initialized object" sıkıntısı olmuyor. Yani 
single == null kontrolü ve single = new ... satırları yer değiştirmiyor.
class MultithreadedSingle {        
  private static volatile MultithreadedSingle single;       
  private MultithreadedSingle() {
  }        
  public static MultithreadedSingle getInstance() {
    if(single==null){
      synchronized(MultithreadedSingle.class){
        if(single==null){
          single= new MultithreadedSingle(); 
        }      
      }
    }
    return single;
  }
}
Eğer lock olarak sınıfı kullanmak istemezsek şöyle yaparız.
private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}
Şimdi niçin synhronized kelimesinden sonra ikinci defa null kontrolü yaptığımıza bakalım.
if (_instance == null) {                // L1
    synchronized (Singleton.class) {    // L2
        if (_instance == null) {        // L3
            _instance = new Singleton();// L4
        }
    }
}
Eğer ikinci defa null kontrolü yapmazsak iki thread şöyle çalışır. Yani bloke olan ikinci thread, synchronized bloğa girince, birinci thread'in yaptığı işi kontrol etmezse, yeni bir nesne yaratır
1. Thread 1 L1'e gelir ve _instance null'dır
2. Thread 2 L1'e gelir ve _instance null'dır
3. Thread 1 L2'de mutex'i kilitler
4. Thread 2 L2'de mutex'i kilitlemeye çalışır ama bloke olur
5. Thread 1 yeni bir instance yaratır ve L4'te atar
6. Thread 1 L2'de kilitlediği mutex'i bırakır
7. Thread 2 L2'de mutex'i kilitler
8. Thread 2 yeni bir instance yaratır ve L4'te atar
9. Thread 2 L2'de kilitlediği mutex'i bırakır
Dolayısıyla hata olur. synchronized kelimesinden sonra ikinci null kontrolü Thread 2'nin yeni bir instance yaratmasını engeller. İşte bu yüzden ismi "Double Checked"

Yanlış bir kod şöyle
class SharedObject {

  private static final Object LOCK = new Object(); //Volatile değil

  private static SharedObject instance = null;

  public static SharedObject retrieve() {
    if (instance == null) {
      synchronized (LOCK) {
        //Burada ikinci null kontrolü yapılmıyor
        instance = create();
      }  
    } 
    return instance;
  }
   
  private static SharedObject create() {
    return new SharedObject();
  }
}
7 - ClassLoader ile beraber private static final field
Bu yöntem Java 1.5'ten önce kullanılıyor. Final bir sınıf ve içinde private static final bir nesne yaratılıyor. private static final olduğu için JVM nesneyi lazy ve thread-safe olarak yaratır. Serialization
public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}





3 Eylül 2019 Salı

PBEKeySpec Sınıfı - Şifreden Hash Üretir

Giriş
Şu satırı dahil ederiz.
import java.security.spec.KeySpec;
import javax.crypto.spec.PBEKeySpec;
PBKDF2 (Password-Based Key Derivation Function 2) algoritmasını gerçekleştirir.
Açıklaması şöyle.
PBKDF2 is one of the oldest KDFs, and is likely the most common. It is not memory hard, which means an attacker can perform massively parallel attacks against it without running into memory issues. PBKDF2 works by taking any keyed hash, typically HMAC, and running it multiple times such that each hash evaluation depends on all the previous hash evaluations. Unfortunately, typical hashes used in HMAC require next to no memory to compute, so an attacker can massively parallelize brute force searches on GPUs and ASICs without running into the issues that a memory-hard KDF would cause.
Açıklaması şöyle
Password-based key-derivation algorithm found in PKCS #5 2.0 using the specified pseudo-random function (<prf>). Example: PBKDF2WithHmacSHA256.
Ne İşe Yarar ?
SecretKeyFactory ile birlikte kullanılarak verilen şifreyi byte[] hash haline getirir.

Örnek
Şöyle yaparız Burada byte[] hash Base64 haline getiriliyor.
String password = "password123";
String salt = "somesalt";
int iterations = 10_000;
int keyLength = 256;

SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password.toCharArray(), 
  salt.getBytes(), iterations, keyLength);
SecretKey key = skf.generateSecret(spec);
byte[] hash = key.getEncoded();

System.out.println(Base64.getEncoder().encodeToString(hash));
Örnek
Şöyle yaparız. Burada byte[] hash Base64 haline getiriliyor.
public class PasswordUtils {

  public static final int ITERATIONS = 1000;
  public static final int KEY_LENGTH = 512;
  private static final String ALGORITHM = "PBKDF2WithHmacSHA512";
  private static final SecureRandom RAND = new SecureRandom();

  public static Optional<String> generateSalt(final int length) {
    byte[] salt = new byte[length];
    RAND.nextBytes(salt);

    return Optional.of(Base64.getEncoder().encodeToString(salt));
  }

  public static Optional<String> hashThePlainTextPassword(
    String plainTextPassword,
    String salt) {
    
    char[] chars = plainTextPassword.toCharArray();
    byte[] bytes = salt.getBytes();

    PBEKeySpec spec = new PBEKeySpec(chars, bytes, ITERATIONS, KEY_LENGTH);
    Arrays.fill(chars, Character.MIN_VALUE);
    
    try {
      SecretKeyFactory fac = SecretKeyFactory.getInstance(ALGORITHM);
      byte[] securePassword = fac.generateSecret(spec).getEncoded();
      return Optional.of(Base64.getEncoder().encodeToString(securePassword));

    } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
      //Handle the exception
      return Optional.empty();
    } finally {
      spec.clearPassword();
    }
  }
}
constructor - password + salt + iterationCount
Şöyle yaparız.
byte[] salt = { ... };
String passPhrase = ...;
int iterationCount = 2;
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
constructor - password + salt + iterationCount + keyLength
AES için şöyle yaparız.
PBEKeySpec spec = new PBEKeySpec("password", "salt".getBytes(),1000  , 128);