İpucu

Basit Bir Konfigürasyon Kütüphanesi Geliştirilmesi



Bir uygulama için en basit konfigürasyon bilgileri Properties'den yüklendiği konfügürasyon sistemidir. Genellikle bir uygulama için tek bir konfigürasyon sınıfı kullanılır ve bilgiler uygulama açıldığında bir kere yüklenir. Bu nedenle Singleton mimarisi kullanılabilir ancak Properties dosyasının path'i başlangıçta verilmesi gerekir. Bu durum Singleton için doğru değildir çünkü farklı parametrelerle farklı nesneler yaratılırması gerekir ki singleton tasarım mantığına aykırıdır. Onun yerine aşağıdaki gibi yapmak tercih edilebilir:

SimpleConfiguration simpleConfiguration=SimpleConfiguration.getInstance();		
simpleConfiguration.load(path);


Bu şekilde tek bir SimpleConfiguration nesnesi olur. Ancak load işleminin unutulmaması gerekir. Ayrıca load yöntemi olur olmaz yerlerde çağrılabilir ve kontrol edilmesi mümkün değildir. Bunun yerine load yöntemi ve getInstance() yöntemi paket level yapıp (sadece paketten erişilsin dışarıdan erişilmesin) bir builder sınıfı kullanılabilir. Bunun için aşağıdaki şekilde SimpleConfiguration elde edilebilir:

SimpleConfigurationBuilder simpleConfigurationBuilder=new SimpleConfigurationBuilder(path);
SimpleConfiguration simpleConfiguration=simpleConfigurationBuilder.build();


Burada SimpleConfigurationBuilder'e properties dosyasının path'i verilir. build() yöntemi çağrıldığında SimpleConfiguration instance'ı yaratılır, load işlemi yapılır ve load edilmiş SimpleConfiguration nesnesi döndürülür. Bu şekilde load() yöntemini çağrılması unutulamaz aynı zamanda builder ile daha sonra istenilen kontroller eklenebilir. Yukarıdaki örneklerin sınıfları aşağıdaki gibi olabilir:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class SimpleConfiguration {
	private Properties properties;
	private static class SingletonHolder {
		public static final SimpleConfiguration instance
			= new SimpleConfiguration();
	}
	
	static SimpleConfiguration getInstance() {
		return SingletonHolder.instance;
	}
	
	public String getProperty(String key) {
		return properties.getProperty(key);
	}
	
	public String getProperty(String key, String defaultValue) {
		return properties.getProperty(key, defaultValue);
	}		
	
	void load(String path) 
			throws FileNotFoundException, IOException {
		properties=new Properties();
		try(InputStream is=new FileInputStream(path)){
			properties.load(is);
		}
	}
}


Builder sınıfı :

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class SimpleConfigurationBuilder {
	private String localfilePath;
	
	public SimpleConfigurationBuilder(String localfilePath) 
			throws FileNotFoundException {
		
		if(localfilePath==null) {
			throw new IllegalArgumentException("localfilePath is null");			
		}
		if(!Files.exists(Paths.get(localfilePath))) {
			throw new FileNotFoundException("localfilePath not found");
		}
		this.localfilePath=localfilePath;		
	}
	
	public synchronized SimpleConfiguration build() throws IOException {			
		
		SimpleConfiguration simpleConfiguration=SimpleConfiguration.getInstance();		
		simpleConfiguration.load(localfilePath);
		return simpleConfiguration;
	}
}


Bu iki sınıf aşağıdaki gibi JUnit 5 ile basitçe test edilebilir:

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import io.github.glytching.junit.extension.folder.TemporaryFolder;
import io.github.glytching.junit.extension.folder.TemporaryFolderExtension;
@ExtendWith(TemporaryFolderExtension.class)
public class SimpleConfigurationTest {
	
	private static String path;
	
    @BeforeAll
    public static void prepare(TemporaryFolder givenTemporaryFolder)
	throws IOException {
        	
		File file = givenTemporaryFolder.createFile("test.properties");
		path=file.getAbsolutePath();
		
		Properties properties=new Properties();
		properties.put("test", "testvalue");
		
		try(OutputStream os =new FileOutputStream(file)){
			properties.store(os,"simple configuration");
		}
		
    }
	
	@Test
	public void loadAndGetPropertyTest() throws IOException {
			
		SimpleConfigurationBuilder simpleConfigurationBuilder=
			new SimpleConfigurationBuilder(path);
		SimpleConfiguration simpleConfiguration=simpleConfigurationBuilder.build();
		
		assertEquals("testvalue", simpleConfiguration.getProperty("test"));
	}
}
prepare() yöntemi ile önce TemporaryFolder içinde bir properties dosyası yaratılır. loadAndGetPropertyTest() ile de test edilebilir. Builder ve SimpleConfiguration için load ve getProperty() yöntemleri ayrı ayrı testler yapılabilirdi ancak basit olması için bu şekilde yapılmıştır.

Bir değil birden fazla Properties dosyasını yüklemek isteyebiliriz. Bunun için SimpleConfiguration sınıfına aşağıdaki gibi bir yöntem ekleyebiliriz :
void merge(String path) throws FileNotFoundException, IOException {
	Properties properties2 = new Properties();
	try (InputStream is = new FileInputStream(path)) {
		properties2.load(is);
	}
	
	Set<Entry<Object, Object>> entries = properties2.entrySet();
	for (Entry<Object, Object> entry : entries) {
	  if(!properties.containsKey(entry.getKey().toString())) {
		  properties.put(entry.getKey().toString(), entry.getValue().toString());	    	  
	  }
	}
}
Bu yöntem ile ikinci bir Properties dosyası ilk properties'e eklenmiş olacaktır. Eğer aynı key varsa dikkate alınmayacaktır. Builder'a ise yeni bir Properties dosyası path'i ekleyip aynı builder'ı dönecek şekilde bir yöntem ekleyelim:
public SimpleConfigurationBuilder withPath(String path){
	paths.add(path);
	return this;
}
builde() yönetimi de aşağıdaki gibi güncelleyelim:

public synchronized SimpleConfiguration build() throws IOException {			
	
	SimpleConfiguration simpleConfiguration=SimpleConfiguration.getInstance();		
	simpleConfiguration.load(localfilePath);
	
	for (String path : paths) {
		simpleConfiguration.merge(path);
	}
	return simpleConfiguration;
}


Bu şekilde build yöntemi ile Properties içerikleri toplanmış olacaktır. Test sınıfı aşağıdaki gibi olabilir:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import com.sca.configuration.simple.SimpleConfiguration;
import com.sca.configuration.simple.SimpleConfigurationBuilder;
import io.github.glytching.junit.extension.folder.TemporaryFolder;
import io.github.glytching.junit.extension.folder.TemporaryFolderExtension;
@ExtendWith(TemporaryFolderExtension.class)
public class SimpleConfigurationTest {
	
	private static String path;
	private static String path2;
	
    @BeforeAll
    public static void prepare(TemporaryFolder givenTemporaryFolder) throws IOException {
        	
		File file = givenTemporaryFolder.createFile("test.properties");
		path=file.getAbsolutePath();
		
		File file2 = givenTemporaryFolder.createFile("test2.properties");
		path2=file2.getAbsolutePath();
		
		Properties properties=new Properties();
		properties.put("test", "testvalue");
		
		try(OutputStream os =new FileOutputStream(file)){
			properties.store(os,"simple configuration");
		}
		
		Properties properties2=new Properties();
		properties2.put("test2", "testvalue2");
		
		try(OutputStream os =new FileOutputStream(file2)){
			properties2.store(os,"simple configuration 2");
		}
		
    }
	
	@Test
	public void loadAndGetPropertyTest() throws IOException {
			
		SimpleConfigurationBuilder simpleConfigurationBuilder=new SimpleConfigurationBuilder(path);
		SimpleConfiguration simpleConfiguration=simpleConfigurationBuilder.build();
		
		assertEquals("testvalue", simpleConfiguration.getProperty("test"));
	}
	@Test
	public void loadMultiplePropertiesAndGetPropertyTest() throws IOException {
			
		SimpleConfigurationBuilder simpleConfigurationBuilder=new SimpleConfigurationBuilder(path);		
		SimpleConfiguration simpleConfiguration=simpleConfigurationBuilder.withPath(path2)
				.build();
		
		assertEquals("testvalue", simpleConfiguration.getProperty("test"));
		assertEquals("testvalue2", simpleConfiguration.getProperty("test2"));
	}
	
}


Önce iki tane Properties dosyası yaratıyoruz. loadMultiplePropertiesAndGetPropertyTest() yöntemi içinde ikinci properties dosyası withPath ile verilmektedir. test key'i ana Properties dosyasındayken, test2 sonradan eklediğimiz Properties içindedir.
zafer.teker , 08.07.2020

Bu Sayfayı Paylaş:

Fibiler Üyelerinin Yorumları


Tüm üyeler içeriklere yorum ekleyerek katkıda bulunabilir : Yorum Gir

Misafir Yorumları




Bu Sayfayı Paylaş:

İletişim Bilgileri

Takip Et

Her Hakkı Saklıdır. Bu sitede yayınlanan tüm bilgi ve fikirlerin kullanımından fibiler.com sorumlu değildir. Bu sitede üretilmiş , derlenmiş içerikleri, fibiler.com'u kaynak göstermek koşuluyla kendi sitenizde kullanılabilirsiniz. Ancak telif hakkı olan içeriklerin hakları sahiplerine aittir