Java ve skript programlama

Önce çözmeye çalıştığım sorundan bahsedeyim. Şirkette yazdığım program ürettiğimiz makineyi kontrol eden başka programla haberleşiyor ve bazı durumlarda onların ayarlanmasını da düzenliyor. Yalnız çok değişik türlü makineler ürettiğimizden bu makinelerin ayarları da aynı şekilde olmuyor. Örneğin üretim bantı hızı her makinede eşit olmuyor. Dolayısıyla bant üzerindeki ürünü inceleyecek kameranın hızının da bu bant hızına göre ayarlanması gerekiyor. Bunun gibi birbiriyle ilişkili parametreleri program dışından, makineye özel ayarlayabilmek için bir konfigürasyon dosyası kullanmıştım. Bu dosya basit ilişkileri programlamak için yeterliydi.

Zamanla makinelerimiz daha da kompleks olmaya başladı. Artık o konfigürasyon dosyası ihtiyacımız olan ilişkileri tanımlayacak durumda değildi. Yeni ilişkileri tanımlamak da bunları programlamamı gerektirecekti. Bunun yerine başka bir çözüm aramaya başladım.

Sonuçta konfigürasyon dosyası da bir tür program olduğundan, bu iş en kolay bir programlama diliyle çözülür diye düşündüm. Örneğin parametreler arasındaki ilişkileri bir programlama diliyle programlasak ve bu ayarlar yapılırken programın içinde bu dosyayı (yani programı) çalıştırıp o ilişkileri doğru kursak o zaman sorun çözülmüş olabilirdi. Burada tabii ki şöyle bir özelliğe de ihtiyacım var. Bu konfigürasyon dosyası java ile yazdığım programdaki bazı nesnelere erişebilmeliydi. Java programının yapması gereken tek şey bu programı çalıştırmak olacaktı.

Kısa bir araştırma sonucu Java 6 ile gelen JSR-223 yoluyla bu işleri yapabileceğimi öğrendim. Yalnız internette yapmak istediğim şeyi anlatan bir yazıya ya da açıklamaya rastlayamadım. Bunun üzerine okuduğum bazı makalelerden, javadoc sayfalarından yola çıkarak küçük bir örnek program yazdım. Bu sayede istediğim şeyin yapılabileceğini görmüş oldum.

İlk iş olarak yjthon.org sitesinden günce jython sürümünü indirdim ve orada anlatılan şekilde kurdum.

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.script.*;

public class Tutorial {
	
	public static class Configuration {
		private Map<String, String> configuration = new     HashMap<>();
		
		public String get(String identifier) {
			return configuration.get(identifier);
		}
		
		public void set(String identifier, String value) {
			configuration.put(identifier, value);
		}

		@Override
		public String toString() {
			return "Configuration [configuration=" + configuration + "]";
		}
	}
	
	private static String getScript() {
		StringBuilder builder = new StringBuilder();
		builder.append("import sys\n");
		builder.append("configuration.set(\"id1\", \"234\")\n");
		return builder.toString();
	}

	public static void main(String[] args) throws ScriptException {
		System.setProperty("python.home", new File(
		        System.getProperty("user.home"), "jython2.7.2").getPath()
		);
		ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
		
		ScriptEngine engine = scriptEngineManager.getEngineByName("python");
		Configuration con = new Configuration();
		ScriptContext context = engine.getContext();
		context.setAttribute("configuration", con, ScriptContext.GLOBAL_SCOPE);
		String script = getScript();
		CompiledScript compiledScript = ((Compilable)engine).compile(script);
		compiledScript.eval(context);
        System.out.println("configuration: " + con.toString());
	}
}

Yukarıdaki program denemeyi yaptığım oldukça küçük bir proje. Configuration sınıfı kabaca java programım ile skrip arasındaki haberleşmeyi sağlayacak nesneyi tanımlayacak. Skrip bu nesne aracılığı ile ana programdaki konfigürasyona erişip tanımlanan ilişkiler yardımıyla bu konfigürasyonu güncelleyecek.

getScript metodu örnek olarak minimum python kodunu tanımlıyor. Bu kod java programından gelen configuration nesnesindeki id1 isimli nesneye 234 değerini atıyor. main metodunda ise önce python.home sistem değişkenini tanımladım. Bu değişken jython kurulumunu yaptığım dizini gösteriyor. Ardından ScriptEngineManager ve ScriptEngine tipinden nesneleri yarattım. ScriptEngine nesnesinin kullandığı ScriptContext nesnesini kullanarak yarattığım Configuration nesnesini (con) skripte configuration adı altında global bir şekilde aktaracağımı bildirdim. Bunu yapmak için ScriptContext sınıfının setAttribute metodunu kullandım.

Ardından bu skripti her ayar değişikliğinde tekrar kullanacağımdan sonraki kullanımlarda zamandan kazanmak için ilk seferde derlemeyi denedim. Bunun için

CompiledScript compiledScript = ((Compilable)engine).compile(script);

satırını kullandım. Bu durumda her kullanımda aynı nesneyi kullanmam gerekecek ama bu kolay bir sorun. Ardından bu skripti çalıştırdım ve java programındaki nesnenin içeriğinin değiştiğini gördüm. Tam da istediğim sonuç.