Nesnelerin Yaratılması ve OCP Prensibi

Bir önceki yazımda CustomerOrder constructor’ı içerisinde nesnenin initialization’ı dışında ProjectElement ile 1:M parent-child ilişkinin kurulmasından bahsetmiştim. Ardından da exists metodunu ProjectElement’e eklemeye kalkarsak ne gibi bir problemle karşılaşırız diyerek yazıyı sonlandırmıştım.

Problem new CustomerOrder(…) ile yeni bir nesne oluşturduğumuz anda ProjectElement nesnesinin child set’inin içerisine, oluşturulan CustomerOrder nesnesinin eklenmesinden ötürü, exists metodunun CustomerOrder nesnesini input parametre aldığı takdirde, hiçbir şekilde belirli bir businessKey ve client değerine sahip bir CustomerOrder’ın ProjectElement child setinde olup olmadığını tespit edememesidir. Exists metodu her seferinde belirtilen CustomerOrder nesnesinin ProjectElement’in child seti içerisinde olduğunu dönecektir.

Görüldüğü üzere CustomerOrder constructor’ının SRP prensibine uymaması sisteme yeni bir özellik eklerken beklenmedik bir durumun ortaya çıkmasına neden oldu. Oysa CustomerOrder constructor’ı sadece nesne initialization’ını yapıp sonlansa idi, exists metodunun signature’unda CustomerOrder’ın kullanılması herhangi bir problem teşkil etmeyecekti.

Burada hemen aklımıza şöyle bir çıkış yolu gelebilir. Exists metodunun görevi belirli bir businessKey ve client değerine sahip CustomerOrder nesnesinin ProjectElement’in child set’inde olup olmadığını tespit etmek ise, metodun signature’ı CustomerOrder almak yerine businessKey ve client değerlerini alacak biçimde tanımlanabilir.

public boolean exists(String businessKey, Client client) {
    for(CustomerOrder co:customerOrders) {
        if(co.getBusinessKey().equals(businessKey) && co.getClient().equals(client)) return true;
    }
    return false;
}

Bu durumda exists metodu görevini yerine getirmiş ve sorun çözülmüş gibi görünüyor. Ancak farz edelim ki, bir süre sonra CustomerOrder’ın kimlik tanımında bir eksik olduğu fark edilsin. Örneğin, String x property’sinin de CustomerOrder’ın tekilliğini belirlemede rolü olduğu anlaşılsın. Bu durumda exists metodunun signature’unda da değişiklik yapmak gerekecektir. Signature’daki bu değişiklik ise exists metodunu kullanan diğer modüllerin de yeniden derlenmelerini gerektirecektir. Bu durum bariz biçimde Open Closed Principle (OCP) olarak bilinen temel bir OO prensibin ihlali demektir.

OCP, sisteme eklenecek yeni bir özelliğin mevcut yapıda bir değişiklik yapmadan, sistemin extend edilerek yapılmasını hedefler. Oysa exists metodunun signature’unda CustomerOrder yerine CustomerOrder’ın tekilliğini belirleyen property’leri kullanarak hem CustomerOrder’ın encapsulation’ını bozmuş olduk, hem de CustomerOrder’ın kimliğinde meydana gelen değişikliklerin doğrudan exists metoduna ve bu metoda bağımlı sistemin diğer kısımlarına da sirayet etmesine neden olduk.

Bu örnekten de görüldüğü gibi temel OOP prensiplerine dikkat etmemiz sistemimizin ilerleyen safhalarında ortaya çıkacak yeni gereksinimlere ve değişikliklere karşı daha sağlıklı cevap verebiliyor olması için oldukça önemlidir. Başlangıçta belki CustomerOrder constructor’ı içerisinde ihlal edilmesi çok önemli birşey gibi durmuyordu. Sonuçta tek satırlık bir işlem olarak, sistemin geneline etki edecek şekilde problemler yaratması pek de olası gözükmüyor olabilir. Ancak küçük görünen işlerin büyük sonuçlar doğurabileceği hiçbir zaman unutulmamalıdır. Atalarımızın dediği gibi, bir mıh bir nal, bir nal bir at kurtatır…

Weird Connection Problems with Spring and Hibernate

At the beginning of this week a colleague of mine said to me that some JDBC connections were left open in one of our web projects. Before continuing to the rest of the story, let me first draw a rough architectural picture of the project.

We use JPA/Hibernate and Spring transactions decleratively in data access layer. In presentation layer, we use Spring WebFlow 1.0.x and keep EntityManager objects open as long as current flow is active to get rid of infamous lazy exception problems.

After all, as we don’t directly deal with JDBC connections in any other point, and have a very standard configuration of Spring with JPA/Hibernate, at first I was sceptical about his observation. We keep EntityManager objects open as long as flows are active, whose duration span user think time. First, I thought that connections are kept open as long as JPA EntityManager objects are open, and there might have been a problem in closing those EntityManager objects in our codebase. However, we quickly concluded that we were closing

EntityManager objects properly, and apart from that, we found out that EntityManager and JDBC Connection open/close operations are not related with each other. EntityManager opens a JDBC connection when it is really needed. Specifically, it opens a new connection when a new transaction begins and closes it at the end of the transaction.

I then said to him that it might most probably be a bug in Hibernate, and suggested to upgrade Hibernate version from 3.2.6 to 3.3.1. Unfortunately, upgrade to latest Hibernate revision didn’t help.

After one or two days of debugging he popped back to me and said that he had found two places which were guilty of those open connections. One place was in Hibernate code. We are using dbtimestamp type which maps to database’s current timestamp, rather than JVM’s current timestamp. In DbTimestampType class, Hibernate uses a PreparedStatement to fetch db’s current timestamp. PreparedStatement is closed and its reference kept inside Batcher object is removed at the end of this operation. However, not closing Resultset retrieved while executing the PreparedStatement causes Hibernate Batcher object to keep reference to the Resultset object. As a result Batcher object doesn’t close JDBC connection as it still has a reference to that Resultset.

There is also an open issue in Hibernate JIRA which is closely related with our case. Instead of waiting for the next release of Hibernate for the solution, we decided to copy DbTimestampType’s source code, fix the problem, and create our own user type namely dbtimestamp2 instead of Hibernate’s registered dbtimestamp type.

The second place which was under suspicion as being cause of those open connections was Spring’s HibernateJpaDialect class. Spring’s JpaTransactionManager exposes JpaTransactionObject as JdbcTransactionObject when DataSource object in

JpaTransactionManager is not null. This might occur in cases when JPA is configured in standalone mode or a DataSource object is injected directly into the JpaTransactionManager. By that way, plain JDBC operations are able to share same transaction context as with JPA operations. JpaTransactionManager does this by first getting Connection object through calling configured JpaDialect’s getJdbcConnection() method, and then bounding DataSource object together with this connection in the current thread context.

If we examine HibernateJpaDialect.getJdbcConnection() method closely, we see that Connection is retrieved by calling Session.connection() method. Session.connection() method returns a borrowed connection which is actually a proxy object to java.sql.Connection. Application code accesses actual JDBC connection through the borrowed connection. Hibernate’s ConnectionManager manages borrowed connection by keeping a reference to it when it is instantiated, and removes the reference when its private cleanup() method is called. cleanup() method actually calls ConnectionManager.releaseBorrowedConnection() method to release it. Another case which triggers borrowed connection release is calling close() method of borrowed connection itself. The conditions at which cleanup() is called method are listed listed in

Hibernate source code as follows;

  • At the end of the Session
  • At manual disconnect of the Session
  • From afterTransaction(), in the case of skipped aggressive releasing

When we look at those three locations, we see that it is called at first two points, however there is no cleanup() call in ConnectionManager.afterTransaction() method. Calling afterTransaction() method causes physical JDBC connection to be closed, but as cleanup() method is not called, borrowed connection is not released after transaction completion.

The problem actually begins after transaction completions. When we trigger a JDBC statement to be executed without a transaction, for example, by accessing a lazy collection’s elements, Hibernate opens a new JDBC connection to fetch collection elements from DB. If connection release mode of Hibernate is set to AFTER_STATEMENT or AFTER_TRANSACTION (provided there is no active TX at that point), then Hibernate attempts to close JDBC connection by calling afterStatement() method. Unfortunately, connection cannot be closed, if borrowedConnection is not null, in other words there is a reference to a borrowed connection object. As you remember this reference is left after previously executed transaction. As a result, the physical JDBC connection will be kept open until a new transaction is initiated or Session is closed, or it is manually disconnected. In our case if user stories have many non transactional data access, or they require long user think time, keeping EntityManager/Session open until the end of flow, might cause connections to be exhausted.

JpaTransactionManager does a resource clean up after transaction completion, and calls JpaDialect’s releaseJdbcConnection() method. However, HibernateJpaDialect does nothing in its releaseJdbcConnection(). If it were called previouly retrieved connection’s close() method, it would cause release of borrowed connection, because as I said before the connection returned from Session was actually borrowed connection, which is a Connection proxy, calling its close method triggers ConnectionManager’s releaseBorrowedConnection.

Üretim Hattında Spring (2)

Makalenin ilk bölümü

Spring Çalışma Zamanı: Kurumsal Servis Katmanı

Spring çalışma zamanı tarafından sağlanan kurumsal servis desteği çekirdeğin (kernel) üzerine kurulmuştur. Spring kapsamlı bir kurumsal servis desteği sunmaktadır. Biz burada en yaygın kullanılan bileşenlerinden bazılarına odaklanacağız.

Transaction Yönetimi

Çalışma zamanındaki transaction yönetim desteği iki faza sahiptir. Uygulama bağlamının (application context) oluşturulması aşamasında transaction alt sistemi @Transactional annotasyonlarını ve “tx namespace”indeki XML tanımlarını işleyerek uygulama bileşenlerinden transaction desteğine ihtiyaç duyanları dekore eder. Çalışma zamanı safhasında ise, bir önceki safhada oluşturulan “transaction interceptor” her transactional operasyonun öncesinde ve sonrasında çağırılır. Interceptor’ün görevi transaction metadatasındaki yayılım (propagation) tanımlarına göre transactionların başlatılması, bitirilmesi, askıya alınması, kaldığı yerden devam ettirilmesi veya gerisin geriye alınması işlemlerini gerçekleştirmektir. Spring transaction yönetim platformu, alt taraftaki sistem transaction yönetim katmanına izolasyon derecelerinin ve salt okunur davranışların düzgün biçimde iletilmesini sağlar.
Transactional bir metod içerisinden bir exception fırlatıldığında, transaction altyapısı mevcut transaction’ın gerisin geriye alınıp alınmayacağına karar verebilmek için exception tipine göre geriye alma kurallarını (rollback rules) yorumlar.
Transaction altyapısında meydana gelebilecek herhangi bir arızanın atomik olmayan güncellemeler yapılması, kirli okumaların (dirty reads) gerçekleşmesi ve veri bütünlüğü ile ilgili diğer arzu edilmeyen durumların ortaya çıkması gibi ciddi sonuçlara yol açacağı aşikardır.

Veri Erişimi

Spring’in veri erişim desteği, sistemde kullanılan teknolojiye bağlı olarak (JDBC, JPA, iBATIS, Hibernate, Toplink etc.) değişebilen birbirinden bağımsız alt sistemlerden oluşmaktadır. Bu veri erişim alt sistemleri aynı transaction içerisinde, örneğin Hibernate ve JDBC, bir arada da kullanılabilir. Spring çalışma zamanı, “persistence” oturumlarını yönetir ve fırlatılan exception’ları kendi DataAccessException hiyerarşisindeki karşılıklarına çevirebilmek için veri erişimi ile ilgili her operasyonda devreye girer.
Spring çalışma zamanı, JDBC tabanlı veri erişiminde ilgili operayonlarda detaylı biçimde – dönen sonuçların iterasyonunun yönetilmesi, SQL verisinin alan nesnelerine çevrimi, saklı yordamların (stored procedures) çağırılması vb işlerde – görev alır.
Veri erişimi ve transaction yönetim altyapısı arasındaki entegrasyon neredeyse bütün kurumsal uygulamaların hayati öneme sahip bir kısmını oluşturur.

Mesajlaşma

Spring’in mesajlaşma desteğinin merkezinde yer alan bileşen “message listener container”lardır. Bu container mesaj tabanlı uygulamalar ve mesajlaşma sağlayıcılar arasında yer alan ara bir katmandır. Bu “container”lar mesaj alabilmek için mesajlaşma sağlayıcılara kayıt olunmasından, mesajların işlenmesi için “thread”lerin yönetilmesinden, ve mesajların uygulama bileşenlerindeki metodlara sevk edilmesinden sorumludur. Mesajlaşma altyapısı, bunların yanında transaction’lara katılmayı, kaynakların temini ve geri verilmesi, ve hataların çevirimi gibi işleri de yürütmektedir.
Bu tarafta meydana gelebilecek arızalar mesajların kaybolmasına veya yanlış biçimde teslimine, vazifeli thread’larin kaybolmasına veya atomik olmayan mesaj işlemlerine yol açabilir.

Güvenlik Yönetimi

Güvenlik yönetimi için Spring Security (Acegi Security) kullanıldığı vakit, Spring çalışma zamanı yine pek çok uygulama bileşeninin işletim hattında yer almaktadır. Web katmanındaki filtreler kimliklendirme (authentication) ve yetkilendirme (authorization) işlerini yönlendirmektedir.
Çalışma zamanı aynı zamanda diğer uygulama katmanlarındaki güvenlik yönetiminden de sorumludur. Dekorasyon fazında ilgili interceptor nesneleri kaydedildikten sonra “secured” olarak tanımlanmış her operasyon bu “interceptor”ler tarafından “intercept” edilir. Çalışma zamanı, rol tabanlı erişimden ve ACL tabanlı güvenlikten de sorumludur. “Pre-invocation interceptor”ler o andaki kullanıcının ilgili metoda erişim yetkisinin olup olmadığını kontrol ederken, “post-invocation interceptor”ler operasyon sonucu dönen nesnelere aktif kullanıcının erişim hakkı olup olmadığını denetler.
Güvenlik altyapısında meydana gelebilecek arızalar sistemdeki kaynaklara ve operasyonlara yetkisiz erişim, yanlış yetkilendirme, ve kimliklendirme sürecinde başarısızlık şeklinde kendini gösterecektir.

JMX Entegrasyonu

Spring, herhangi bir MBean nesnesini veya normal bir uygulama bileşenini JMX’e dahil etmeyi oldukça kolay kılmaktadır. JMX alt sistemi, mevcut herhangi bir MBean veya MXBean nesnesini bir MBean sunucusuna (MBeanServer) kayıt ettirebilir. Herhangi bir yönetim arayüzüne sahip olmayan normal uygulama bileşenlerinin bu sunuculara kayıt işlemi de oldukça kolaydır. JMX alt sistemi bu tür bileşenler için çalışma zamanında otomatik olarak ModelMBean nesneleri oluşturmaktadır. Spring’in JMX desteği, uygulama hakkındaki operasyonel bilginin JMX vasıtası ile erişilebilir kılınması için oldukça kolay bir yoldur.

WEB İstekleri

Spring çalışma zamanının web isteklerinin işlenmesine müdahil olması kullanılan “web framework”üne göre değişmektedir. Spring, istek(request) ve oturum(session) kapsama alanındaki bileşenleri her zaman yönetmektedir. Böyle bir bileşen üzerinde herhangi bir operasyon çağırıldığı vakit, gerekiyorsa yeni bir nesnenin oluşturulması ve isteğin doğru bileşene sevk edilmesi sağlanır. Spring çalışma zamanı genellikle web sayfasındaki verinin alan nesnelerine bağlanması (data binding) ve verinin doğrulanması (validation) işlemlerinde de görev almaktadır. Pek çok yeni nesil web uygulaması Spring Web Flow üzerine bina edilmektedir. Spring Web Flow, web uygulamalarındaki akışları ve bu akışlar arasındaki geçişleri yöneten önemli bir bileşendir.
Web altyapısındaki arızalar yanlış veya güncel olmayan (stale) bir bilgiye erişilmesine, yada bilginin kaybolmasına neden olabilir, girdinin uygulamadaki alan nesnelerine doğru biçimde dönüştürülememesine veya benzer durumlara yol açabilir.

Spring Çalışma Zamanı: Akort (Tuning)

Spring ile desteklenmiş bir uygulamanın performansını nasıl geliştirebilirsiniz?

Böyle bir uygulama üzerinde herhangi bir değişiklik yapmadan önce ilk yapılması gereken düşük performansın nedeni olabilecek noktaları belirlemek için ölçümleme yapmak ve önerilen değişikliklerin getireceği faydaları nicel olarak tespit etmektir. Bu aşamadan sonra optimizasyonlar iki temel kategoriye girmektedir:1. Etkin bir uygulama planının oluşturulması (uygulama konfigürasyonunun ve ayarların akortlanması), 2. Çalışma zamanındaki yeteneklerin etkin biçimde kullanılması (Uygulamanızın tasarımının optimize edilmesi).Uygulama geliştirmeye en açık ve temiz tasarımla başlayın, Spring’in sunduğu yetenekleri tam kapasite kullanın ve bunlardan farklı bir yola ancak rakamlar gerçekten bir fayda işaret ettiği vakit başvurun.

Önce Ölçümleyin

Herhangi bir akortlama çalışması için başlangıç noktası ölçümlemedir. Apache JMeter, Selenium gibi araçlar ve bir profiler burada faydalı olacaktır. Interface21 danışmanları JAMon ve Spring AOP yada AspectJ’yi bileşenler üzerindeki işlemleri ve isteklerin işletim yollarını incelemek için birleştirmede başarılı sonuçlar almışlardır.
Katmanlara göre ayrı ayrı ölçümleme yapın ki, örneğin web katmanında içeriğin gösterimi için harcanan zamanla, herhangi bir başka katman ve onun daha aşağısında geçen zaman arasında bir kıyas yapabilesiniz.

Etkin Bir Uygulama Planı (Blueprint) Oluşturun

Etkin bir uygulama planı oluşturmanın sırrı sisteminizin kurulacağı hedef platformun bütün avantajlarını kullanmaya çalışmaktır. Bunu gerçekleştirmek Spring’in ortamsal bağımlılıkları uygulama kodunuzun dışında tutmasından ötürü eskiye nazaran çok daha kolaydır.

Eğer bir uygulama sunucu üzerinde çalışıyorsanız sistemin yönetim arayüzü üzerinden oluşturulmuş veritabanı bağlantı havuzunu kullanabilirsiniz. Spring’i, bu havuzun referansına JNDI üzerinden erişecek biçimde ayarlayabilirsiniz. Örneğin,

	
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${jdbc.driverClass"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

yukarıdaki dataSource bileşen tanımını kullanmak yerine aşağıdaki tanımı kullanmanız daha doğru olacaktır.

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/>

Yukarıdaki tanımı kullanmanız size iki avantaj sağlayacaktır: Birincisi uygulamanın bakımını uygulama sunucusu yönetim arayüzünden yapmak operasyonel ekipler için daha kolay olacaktır. İkincisi ise uygulama sunucu üreticisi bağlantı havuzunu pek muhtemelen kendi platformu için optimize etmiştir.
Aynı şekilde, JMS ile çalışırken de uygulama sunucusu üzerinde ayarlanmış JMS ConnectionFactory ve Destination nesnelerine JNDI ile erişmeniz daha doğru olacaktır. Burada ilaveten uygulama sunucusunun ConnectionFactory nesnesi sadece JMS bağlantıları için değil, diğer ara katman JMS kaynakları için de, örneğin oturumlar ve mesaj üreticiler gibi, havuzlama yapıyor olacaktır. Bu diğer JMS kaynaklarının da havuzlanması ile JMS sağlayıcının en yüksek debisine erişmeniz daha büyük bir ihtimaldir. Bu kaynakların yönetilmediği bir Java EE ortamında ise Spring’in SingleConnectionFactory nesnesi aynı bağlantıyı tekrar tekrar kullanacaktır. Eğer üreticiniz havuzlama kabiliyetine sahip bir ConnectionFactory nesnesi sağlıyorsa bunun avantajlarından sonuna kadar yararlanmanız en doğrusu olacaktır. Bu JmsTemplate kullanarak mesaj gönderme sırasında ortaya çıkabilecek en büyük performans darboğazını ortadan kaldıracaktır.
Transaction yönetimi için eğer Spring sizin kurulum ortamınız için özel bir platform transaction yöneticisine sahip ise mutlaka bunu kullandığınızdan emin olun. Örneğin, Spring WebLogic tarafından geliştirilen transaction ortamının avantajlarını tam manası ile kullanabilmeyi sağlayan WebLogicJtaTransactionManager bileşenini sunmaktadır. Aynı şekilde WebSphere için WebSphereUowTransactionManager ve OC4J için de Oc4jJtaTransactionManager bileşenleri mevcuttur. Eğer bu ortmalardan birine kurulum yapıyorsanız, standart JtaTransactionManager bileşeni yerine bunlardan birini tercih etmelisiniz. Spring 2.5 ile birlikte gelen elemanı mevcut ortamı otomatik olarak algılayıp uygun bileşeni seçecektir.
Sunucu üzerinde ayarlanmış bileşenleri JNDI vasıtası ile kullanmak kurulum ortamınızın imkanlarından tam kapasite yararlanmanızı sağlayacaktır. Bununla birlikte, hala uygulama sunucusu dışında da entegrasyon testlerinizi çalışırabiliyor olmalısınız. Örneğin, ActiveMQ’yu kullanarak mesajlaşma ile ilgili basit bazı entegrasyon testleri çalıştırmak, fakat sisteminizi IBM MQSeries ile kurmak isteyebilirsiniz. Bu tür ayarlamalar Spring’in birden fazla ayar dosya desteği ile kolaylıkla gerçekleştirilebilir. Ortama bağlı her türlü ayar bilgisini uygulamanın ana ayar bilgilerinden ayrı tutmanızı öneririz. İyi pratiklerden biri, her bir uygulama modülü için ayrı bir ayar dosyası oluşturmaktır. Bunlara ilave olarak, örneğin integration-test.xml veya production.xml gibi bir ayar dosyaları da oluşturabilirsiniz. Entegrasyon testleri sırasında diğer uygulama modüllerinin ayar dosyaları ile birlikte integration-text.xml dosyasını kullanarak, üretim ve sistem testleri sırasında da bu dosyayı production.xml dosyası ile değiştirerek bir uygulama bağlamı (application context) oluşturabilirsiniz.
Spring’in PropertyPlaceholderConfigurer bileşeni ayar bilgilerinin operasyonel ekiplerin müdahale edebilmesini sağlayacak biçimde ayar dosyalarından dışarı çekilebilmesi için güzel bir çözümdür. Interface21 danışmanlarının başarılı biçimde uyguladıkları bir yöntem properties dosyalarını aşağıdaki gibi zincirlemeye tabi tutmaktır:

  1. classpath*:*.properties.local: Bu properties dosyaları kaynak kod yönetim sistemine dahil edilmeyip, yazılım geliştiricilerin mevcut ayarları kendi ortamlarına göre değiştirebilmelerini sağlar.
  2. classpath*:META-INF/*.properties.default: Bu properties dosyaları kurulum sırasında üretilen uygulama ara ürünlerine dahildir ve varsayılan ayarları barındırmaktadır. Bazen proje ihtiyaçlarına göre bu kısım göz ardı edilebilir.
  3. classpath*:*.properties: Bu properties dosyaları herhangi bir uygulama ara ürününe dahil değildir ve operasyonel ekiplerin ayarları kolayca değiştirmesini sağlar.
<context:property-placeholder location="classpath*:META-INF/*.properties.default,classpath*:*.properties,classpath*:*.properties.local"/>

Burası için güzel bir ip ucu da Spring’in JMX desteğini kullanarak bir MBean oluşturup bütün ayar bilgilerini JMX vasıtası ile dışarıdan erişilebilir kılmak olabilir. Bu sayede çalışan bir uygulamaya bağlanarak kolaylıkla o anda kullanılan ayar bilgilerinin neler olduğunu görebilirsiniz.

Bütün bunlara ek olarak etikin bir uygulama planı oluşturmak için üzerinde düşünülmesi gereken diğer noktalar şöyle sıralanabilir:

  • Kullandığınız JDBC bağlantı havuzu için en uygun bağlantı sayısını tespit edin. Bu aşamada gerçekçi bir üretim durumu senaryosu kullandığınızdan emin olun.
  • Spring’in TaskExecutor yapısını bir thread havuzu ile beraber kullanıyorsanız uygun bir thread havuz büyüklüğü belirleyin. İşlem yoğun işler için CPU sayısına denk miktarda thread iyi bir başlangıç olabilir. Eğer işleriniz yerel dosya sistemine erişip I/O gerçekleştiriyorsa bir thread ekleyin. Ağ üzerindeki I/O işlemleri için birden fazla thread ekleyin. TaskExecutor yapısını WebSphere veya WebLogic üzerinde kullanıyorsanız mevcut ortamın avantajlarından yararlanmak için (örneğin, kendi kendini akortlayan havuzlar) ve entegrasyon amacı ile CommonJWorkManagerTaskExecutor sınıfını kullanın.
  • Transaction demarcation ayarları yapılırken salt-okunur transactionlar için salt-okunur özelliği kullanın. Bu ayar Hibernate kullanılan, veritabanından pek çok nesnenin okunduğu fakat herhangi bir değişikliğin yapılmadığı transactionlar için ciddi bir performans artışı sağlayacaktır. Salt-okunur özelliğin aktive edilmesi Hibernate oturumunun FlushMode.NEVER’a set edilmesini sağlayacaktır. Bu da Hibernate oturumundaki nesnelerin oturum sonlandırılırken herhangi bir değişiklik yapılıp yapılmadığına yönelik kontrole tabi tutulmaması demektir.
  • 2-PC ihtiyacınız yoksa (tek bir kaynak yöneticisi sözkonusu ise) yerel transaction yöneticisi kullanın. Spring, HibernateTransactionManager’ı kullanarak JDBC ve Hibernate ile gerçekleştirilen veri erişimlerini aynı transaction içinde kolaylıkla kontrol edebilir. Bu birden fazla erişim modunun, fakat sadece bir kaynak yöneticisinin kullanılması durumuna denk düşmektedir.
  • Message listener container’lar için kurulum ortamının JMS transaction’larını “acknowledge=transacted” özelliğini set ederek kullanın.

 

Çalışma Ortamının Optimizasyonlarından Yararlanın

Pek çok kurumsal uygulamanın performans problemleri persistence katmanına dayanır. Bu tür uygulamalardaki iyi performans, sıklıkla sağlam tasarım kararlarının bir sonucudur. Bu durumlar için bazı faydalı ipuçları şöyle sıralanabilir:

  • ORM aracı kullanırken “eager” ve “lazy” yükleme startejilerini uygun dengeli biçimde kullanmaya özen gösterin. Varsayılan özellik olarak “lazy” yüklemeyi kullanın, daha sonra “eager” yüklemeden faydalanabilecek durumları akortlamak için “fetch join”leri kullanın. Sorguları akortlarken “şu andan itibaren üretim hattında 1 sene” boyunca ihtiyaçları karşılayabilecek şekilde akortlama yapın.
  • ORM aracınızın veya veritabanının imkanlarından yararlanarak SQL ifadelerini log’larınıza basın. Bu şekilde ne zaman çok fazla sorgunun işletildiğini tespit etmeniz gayet kolay olacaktır.
  • Hibernate kullanıyorsanız Hibernate Statistics nesnesinden çalışma zamanında neler döndüğünü anlamak için yararlanın. Statistics nesnesine, program kodu içerisinden veya bu nesneyi Spring kullanarak MBean sunucusuna MBean olarak kayıt ettirerek erişebilirsiniz. Statistics nesnesini JUnit testleri ile birlikte programatik olarak kullanabilir, kaç tane sorgu çalıştırılacağını sınayıp, en fazla kaç sorguya tolerans gösterebileceğinizi belirleyebilirsiniz.
  • Toplu işlemlerde, toplu ekleme veya güncelleme gibi, ve saklı yordamlarda ORM yerine JDBC’yi (Spring JDBC aracılığı ile) kullanmanız en doğrusudur. Spring Hibernate ve JDBC veri erişim yöntemlerinin aynı transaction içerisinde beraber kullanılmasını oldukça kolaylaştırmaktadır. Sadece aynı tablolar üzerinde çalışıyorsanız doğrudan JDBC ile çalışmaya başlamadan evvel Hibernate oturumunuzu boşalttığınızdan (flush) emin olmanız yeterlidir.
  • Veritabanınızın sağladığı olanakları en üst düzeyde kullandığınızdan emin olunuz.
    • Bir uygulamada çok büyük bir excel dosyasını okuyup, kayıtları basit bir dönüşüme tabi tuttuktan sonra SQL Server’daki bir tabloya eklememiz gerekiyordu. En iyi akortlama gayretimize rağmen bu işlem 3 saati buluyordu. SQL Server’ın “linked” sorgu kabiliyetinden yararlanıp, Excel dosyasını ODBC ile erişilen bir veri kaynağı gibi ele aldığımızda ve dönüşümle ilgili iş mantığını da saklı yordama taşıdığımızda süre sadece 17 sn’ye düşmüştü.
    • Derinliği belirsiz bir ağaç yapısı üzerinden dolaşma ihtiyacı olan başka bir uygulama ise Hibernate ile yazılmıştı. En iyi çabalarımıza rağmen sorgu saatler sürüyor ve sonunda “Out Of Memory” hatası alınıyordu. Oracle saklı yordamları ve hiyerarşik sorgu desteği ile bu işlem 5 sn altında tamamlanmıştı.
    • Eğer normal bir dosyayı Oracle veritabanına yüklemeniz gerekiyorsa veriyi Oracle’ın SQL Loader’ı ile ara bir tabloya attıktan sonra bir saklı yordam ile bu tablodaki veriyi işleyip asıl tablolara transfer edebilirsiniz.
  • Eğer bir operasyon tamamen persistence mantığından oluşuyorsa (başka bir deyişle herhangi bir iş mantığı içermiyorsa) bu durumda onu bir saklı yordama taşımayı ve bu saklı yordamı da Spring JDBC ile çağırmayı düşünebilirsiniz.
  • Salt okunur referans veriyi ön bellekte tutulabilirsiniz.

Toplu işlem (batch) uygulamalarda bellek kullanımı çok önemli bir unsur olduğundan bu konuya ayrıca dikkat etmek gerekmektedir. Sonuç olarak çok fazla miktarda bellek tüketip, maliyeti yüksek “garbage collection” duraklamalarından kaçınmanız gerekecektir. “Stream” tabanlı algoritmalar bu tür uygulamalar için en uygun yaklaşımdır. Örneğin, bu türr uygulamalarda “collection”lar yerine “iterator”ları kullanmalısınız. Eğer satırları bölmek istiyorsanız karakter tabanlı bölümlemeyi “String” tabanlı bölümlemeye tercih etmelisiniz. Bu yaklaşımı kullanarak 2.5 milyon satırlık bir dosyayı sadece 120K’lık bir bellek kullanarak 4 sn’nin de altında bir sürede okuyup, yorumlayıp, işlemeyi başardık.
XML kullanılan toplu işlem uygulamaları için de “stream” yaklaşımı uygundur. Daha önceki bir uygulamada 280 MB’lık bir dosyada 100,000 karmaşık XML olayını işleme durumu ile karşı karşıya kaldık. Bu işlem DOM tabanlı yaklaşımda, her olay için bir DOM ağacı oluşturularak gerçekleştirildiğinde 2.5 saat sürüyor ve 9 dk’ya varan “garbage collection” beklemelerine neden oluyordu. XML “pull” tabanlı bir yorumlamaya dönerek bütün girdiyi sadece 200 K’lık bir bellek ile 3 sn’de işlemeyi başardık.
Burada başka bir faydalı ipucu da birim ve entegrasyon testleri sırasında java.lang.management paketindeki JVM istatistiklerini kullanmaya çalışmaktır. Bu yöntem size CPU ve “garbage collection” zamanı vs. ile ilgili kontroller yapabilmeyi sağlar.
Son bir öneri de veri katmanınızı akortlama ile ilgilidir. Her takım elinin altında iyi bir DBA’ya sahip olmaktan faydalanacaktır.
Interface21 danışmanları tarafından derlenen çalışma zamanı akortlaması ve optimizasyonları ile ilgili öneriler şöyle sıralanabilir:

  • Spring Batch projesindeki tekrar deneme desteği, başarısız sonuçlanan operasyonları tekrar denemek için kullanılabilir. Bu yöntem son kullanıcılara yansıyan arızaların sayısını azaltarak operasyonel yükü hafifletebilir.
  • Web içeriğinin üretilmesinin (render) maliyetini hafife almayın. Bu işlemi kesinlike bir transaction dışında gerçekleştirmelisiniz.
  • Her web isteği için tekrar tekrar uygulama bağlamı (application context) oluşturmayın. (Legacy uygulamaları Spring’e taşırken zaman zaman karşılaşılan bir durumdur.)
  • Arka planda çalıştırılabilecek işlerde kullanıcı bekleme zamanını azaltmak için Spring’in asenkron “task executor”unu kullanmayı düşünün.
  • Uygun bir “remoting” protokolünü tercih edin. SOAP ile birlikte çalışabilirlik ihtiyacınız yoksa, Spring’in HttpInvoker desteği basit bir durum için daha basit ve hızlı olacaktır.
  • Uygulamanızın geniş bir bölümünü etkileyen “aspect”ler varsa bunları çalıştırmak içim Spring AOP yerine AspectJ kullanmayı düşünün.

Interface21 danışmanlarının akortlama ile ilgili faydalı buldukları bazı kaynaklar:

  • Thomas Kyte’ın “Runstats.sql” test harness
  • “Effective Oracle by Design” (Thomas Kyte)
  • “Java Performance Tuning (Jack Shirazi)
  • Sun Java Performance Guides

Özet

Spring çalışma ortamı uygulama bileşenleri için kapsamlı bir servis seti sunmaktadır ve üretim hattında uygulamaların çalıştırılmasında kritik bir rol oynamaktadır. Bu servisler çekirdek uygulama bileşen yönetimi, transaction’lar, güvenlik, veri erişimi vb. içermektedir.

Bu makale operasyonel ekiplere Spring uygulamalarının çalışma zamanındaki davranışlarını daha iyi anlamaya, ve olabilecek aksaklıkları gidermeye yönelik bir kılavuzluk sağlamaktadır. Spring destekli bir uygulamayı akortlama ile karşı karşıya kaldığımızda yapılması gerekenler ilk önce ölçümleme, ardından etikin bir uygulama planı oluşturma ve son olarak da etkin bir tasarım ile çalışma ortamının optimizasyonlarından faydalanmaktır.
Çalışma zamanındaki servislerin düzgün bir fonksiyonaliteye (hatta çalışır bir fonksiyonaliteye) sahip olmasının hayatiyet arz eden niteliği dikkate alındığında, Spring yüksek standartlarda bir mühendislik ürünü olup, kalitesi ile ilgili sağlam bir üne sahiptir. Dünya genelindeki binlerce Spring destekli uygulama bunun bir kanıtıdır.

Teşekkür ve Bilgilendirme

Pek çok Interface 21 çalışanına bu makale için akortlama ile ilgili verdikleri ip uçları ve öğütler için teşekkürler.

Java ve JMX, ABD ve diğer ülkelerde Sun Microsystems’in tescilli markasıdır.

OSGI ABD ve diğer ülkelerde OSGI Alliance’ın tescilli markasıdır.

IBM, MQSeries, ve WebSphere, ABD ve diğer ülkelerde IBM’in tescilli markasıdır.

Oracle, Oracle Corporation ve ortaklarının tescilli markasıdır.

Atıflar

  1. Apache JMeter – http://jakarta.apache.org/jmeter/
  2. JAMon – http://jamonapi.sourceforge.net/
  3. Selenium – http://www.openqa.org/selenium/
  4. Runstats.sql (Thomas Kyte) – http://asktom.oracle.com/tkyte/runstats.html
  5. “Java Performance Tuning”, Jack Shirazi
  6. “Effective Oracle by Design”, Thomas Kyte
  7. Java performance guides http://java.sun.com/docs/performance/

Çalışan ve İşveren Açısından Danışman

Sanırım bizim sektörümüzde danışman terimi için herkesin kafasında farklı bir imaj beliriyor. Çalışanlar açısından danışman demek çoğu zaman akıl vererek para kazanmak anlamına geliyor. Danışmanlık yapmak elini taşın altına koymadan, siz söyleyeceksiniz başkaları yapacak gibi hayal ediliyor. İşverenler açısından ise danışman kendi bünyesindeki personele yaptıramadığı her türlü işi, her ne kadar absürd olursa olsun, yapacak personel gibi algılanıyor. İşveren danışmanlarla çalışırken her türden isteklerinin karşılanmasını beklerken cebinden de en az paranın çıkmasını arzu eder. Anlayacağınız, tam da şu karanlıkta kalmış birkaç kişinin filin değişik yerlerini elleyerek fili tanımlamaya çalışmaları gibi bir durum söz konusudur.
Sanırım bizim sektörümüzde danışman terimi için herkesin kafasında farklı bir imaj beliriyor. Çalışanlar açısından danışman demek çoğu zaman akıl vererek para kazanmak anlamına geliyor. Danışmanlık yapmak elini taşın altına koymadan, siz söyleyeceksiniz başkaları yapacak gibi hayal ediliyor. İşverenler açısından ise danışman kendi bünyesindeki personele yaptıramadığı her türlü işi, her ne kadar absürd olursa olsun, yapacak personel gibi algılanıyor. İşveren danışmanlarla çalışırken her türden isteklerinin karşılanmasını beklerken cebinden de en az paranın çıkmasını arzu eder. Anlayacağınız, tam da şu karanlıkta kalmış birkaç kişinin filin değişik yerlerini elleyerek fili tanımlamaya çalışmaları gibi bir durum söz konusudur.

Nesnelerin Yaratılması ve SRP Prensibi

Geçen gün şu sıralar birlikte çalıştığım bir müşterimin projesinde şöyle bir durumla karşılaştım. ProjectElement ve CustomerOrder adında iki entity arasında 1:M parent-child ilişki söz konusuydu. ProjectElement’in tekilliğini businessKey ve client gibi iki değişken belirlerken, CustomerOrder’ın tekilliğini ise businessKey, client ve nesnenin ait olduğu projectElement’i belirliyordu. Başka bir ifade ile CustomerOrder nesnesinin, yaşam döngüsü boyunca tek bir projectElement’e ait olması gerekiyordu. CustomerOrder constructor’ı şu şekilde tanımlanmıştı.

public CustomerOrder(String businessKey, Client client, ProjectElement projectElement) {
      this.businessKey = businessKey;
      this.client = client;
      this.projectElement = projectElement;
      this.projectElement.getCustomerOrders().add(this);
  }

Görüldüğü gibi parent-child ilişki CustomerOrder constructor’ı içerisinde kuruluyor ve nesne yaratıldıktan sonra bir daha bu ilişkinin değiştirilmemesi isteniyordu. İlişkinin kurulması görevinin CustomerOrder’ın constructor’ına verilmesinin “single responsibility” prensibi (SRP) ile çeliştiğini düşünüyorum.

SRP bir sınıf veya metot düzeyinde tek bir sorumluluğun üstlenilmesini ister. Herhangi bir sınıf sadece belirli bir sorumluluğu yerine getirmeye odaklanmalıdır. Yada bir metot sadece tek bir görevi gerçekleştirmelidir. Java’daki sınıf constructor’larının normal metotlardan pek bir farkı yoktur. Constructor’lar nesneler oluşturulurken çağırılırlar ve nesnelerin düzgün biçimde yaratılıp kullanıma hazır hale gelmesini sağlarlar. Yani constructor (metotlarının) görevi nesnenin yaratılmasından ibaret olmalıdır. Yukarıda ise nesnenin düzgün biçimde işlevini yerine getirebilmesi için gerekli initialization işlemleri yapıldıktan sonra ilaveten parent-child ilişkisinin kurulması görevi de constructor içerisine verilmiş. Bu durumda constructor iki farklı görev üstlenmiş oluyor.

  1. CustomerOrder nesnesinin yaratılması
  2. ProjectElement-CustomerOrder ilişkisinin kurulması

Oysa constructor’ın aşağıdaki gibi yazılması yeterlidir.

public CustomerOrder(String businessKey, Client client, ProjectElement projectElement) {     
	this.businessKey = businessKey;     
	this.client = client;
	this.projectElement = projectElement; 
}

Parent-child ilişkinin kurulması görevi ise ya ProjectElement’e ya da bir Factory metoda verilebilir.

public class CustomerOrderFactory {
    public CustomerOrder create(String businessKey, Client client, ProjectElement projectElement) {
        CustomerOrder co = new CustomerOrder(businessKey,client,projectElement);
        projectElement.getCustomerOrders().add(co);
    }
}
public class ProjectElement {
    public void addCustomerOrder(CustomerOrder co) {
        customerOrders.add(co);
    }
}

SRP prensibinin ihlal edilmesi, bu metotları uygulama içerisinde değişik yerlerde kullanırken istenmeyen durumlarla karşılaşılmasına, metodun asıl fonksiyonalitesine ihtiyaç duyulurken onun yanında o durumda ihtiyaç duyulmayan veya gerçekleşmemesi gereken diğer işlemin yapılması nedeni ile asıl senaryonun görevini yerine getirememesine neden olabilir.

Yeni bir senaryo ile bu durumu örneklemeye çalışalım. Projede ilk CustomerOrder constructor’ının kullanıldığı var sayılarak ProjectElement sınıfına o anda belirli bir CustomerOrder nesnesine sahip olup olmadığının sorulmasını sağlayan bir metot eklemeye çalışalım.

public boolean exists(CustomerOrder co) {
    return customerOrders.contains(co);
}

Sizce burada karşılaşacağımız problem nedir? Peki problemi çözmek için nasıl bir yol izleyebiliriz? Bu yazı yeterince uzadı, bir sonraki yazıda bu konuya devam edeceğim. Sizin içinde bu ara, problemi ve çözüm yollarını düşünme şansı olsun 🙂

How to Attach Zipped Content While Sending Mail

One of the things I most like about Spring is its approach in bringing a high level, easy to understand and use interface and configurability to several gradually lower level APIs of Java, such as JavaMail, JMS, JNDI and so on. Several days ago, a collegue of mine asked me whether I had any experience with sending mails using JavaMail, but also attaching some runtime created content after zipping it.

Since I met with Spring Application Framework, I never attempt solving problems directly without first reviewing Spring Reference to see if it provides any solution for my problem at hand; and mostly it does!

This time it is about using JavaMail to send attached mails. Well, Spring has a nice layer on top of JavaMail API, to configure and send several types of mails, ranging from simple content, to html content with inline resources. To use JavaMail in your program via Spring support, you only need to configure a bean with class org.springframework.mail.javamail.JavaMailSenderImpl, and reference it either via its MailSender or JavaMailSender interfacein your code. MailSender interface is just for sending simple mail messages.If you need MIME support, for example, to attach file as in our case here, you will need to access the mailer bean through its JavaMailSender interface.

	
<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host">
        <value>localhost</value>
    </property>
    <property name="port">
        <value>25</value>
    </property>
    <property name="username">
        <value>ksevindik</value>
    </property>
    <property name="password">
        <value>secret</value>
    </property>
    <property name="javaMailProperties">
        <props>
            <prop key="mail.smtp.auth">false</prop>
        </props>
    </property>
</bean>	

I will show you a simple test case to send a mail using javaMailSender bean configured above. I like to inject beans using @Autowired annotation in my Spring integration tests. You don’t need any setter method at all. You can inject dependencies to private properties.

@Autowired
private JavaMailSender javaMailSender;

@Test
public void sendMailWithAttachmentContentPreparedOnTheFly() throws Exception {

MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom("ksevindik@ems.com");
helper.setTo("developers@ems.com");
helper.setSubject("on the fly attachment test");
helper.setText("this is normal message body text");

Up to here, all we did is creating a javax.mail.internet.MimeMessage object from javaMailSender bean, then wrapping it with org.springframework.mail.javamail.MimeMessageHelper, and setting its from, to, subject properties, and writing message body via this helper. This is very similar to sending simple mail messages. MimeMessageHelper does most of the hard work. Apart from attachments, it also has support for html content, and inline images etc. It provides with several methods for adding attachments to the current message. What we need to do here is to deliver attachment content as a stream which is created on the fly.

ByteArrayOutputStream bout = new ByteArrayOutputStream();
ZipOutputStream zout = new ZipOutputStream(bout);
zout.putNextEntry(new ZipEntry("test.txt"));
zout.write("this is zipped content".getBytes("utf-8"));
zout.closeEntry();
zout.close();
helper.addAttachment("test.zip", new ByteArrayResource(bout.toByteArray()));

First, we created zipped content using Java ByteArrayOutputStream and ZipOutputStream classes, then passed the content as org.springframework.core.io.ByteArrayResource to MimeMessageHelper. Content type is determined from given filename (“test.zip”) which actually for this case doesn’t exist physically.

SimpleSmtpServer mockSmtpServer = SimpleSmtpServer.start(25);
javaMailSender.send(message);
mockSmtpServer.stop();

I use Dumbster fake smtp server for testing purposes. After starting smtp server, I just send mail via javaMailSender, and then stop the server again.

TestCase.assertEquals("There should be one received message",1,smtpServer.getReceivedEmailSize());
SmtpMessage receivedMessage = (SmtpMessage) smtpServer.getReceivedEmail().next();
TestCase.assertTrue(receivedMessage.toString().contains("Content-Type: application/zip; name=test.zip"));

We can check Dumbster if any message is received, and check message headers, or body against what we expect to be in there. As you see, you are just isolated from all the hard work related with preparing MimeParts and other boring issues by using MimeMessageHelper. Is it rocket science? Definetely not, but you most probably are not paid for playing constantly with such middleware stuff either!

Üretim Hattında Spring (1)

Yazar: Adrian Colyer, CTO, Interface21 October 2007

Orjinal makale: Spring In Production

Çeviri ve Türkçe’ye uyarlama: Kenan Sevindik
Spring Framework ve Spring Portföy’ündeki ürünler dünya genelinde ki her tür endüstride, yaşamsal öneme sahip pek çok uygulamada kullanılmaktadır. Spring programlama ve konfigürasyon modeli net biçimde anlaşılır, ve dokümante edilmiş olup, bugün dünya genelinde binlerce uygulama geliştirici tarafından tecrübe edilmiştir. Bu makale, bu tür uygulamaları yöneten “operasyonel ekipler” için yazılmıştır. Spring’i farklı bir açıdan ele almaktadır; Spring çalışma zamanı (runtime)ortamı ve kurumsal uygulamaların işletilmesinde bu ortamın oynadığı kritik rol.

Giriş

Spring Framework ve Spring Portföy’ündeki ürünler dünya genelinde ki her tür endüstride, yaşamsal öneme sahip pek çok uygulamada kullanılmaktadır. Örneğin, İngiltere’deki hemen hemen bütün bankalararası transfer işlemleri Voca isimli bir firma tarafından yönetilmektedir. Voca, yoğun günlerde 80 milyonun üzerinde operasyonu işlemekte, İngiliz halkının %70’inden de fazla bir kesiminin fatura, ve %90’lık bir kesiminin de maaş ödemelerini idare etmektedir. Voca, bugüne kadar tek bir ödeme ile ilgili işlemi kaybetmemesi ile övünmektedir. Bütün bu operasyonlar Spring ile güçlendirilmiştir. Örneğin, yakın bir zamanda HSBC Interface21 ile üretim hattındaki uygulamalarında hayati önem arz edecek derecede Spring kullanmaya yönelik bir destek anlaşmasına girmiştir. Diğer bir örnek,dünya üzerindeki kargo hareketlerinin %25’inin yönetimi Navis tarafından, Spring destekli bir uygulama ile gerçekleştirilmektedir. Spring programlama ve konfigürasyon modeli net biçimde anlaşılır, ve dokümante edilmiş halde olup, dünya genelinde binlerce uygulama geliştirici tarafından kullanılmaktadır. Bu makale, bu tür uygulamaları yöneten “operasyonel ekipler” için yazılmıştır. Spring’i farklı bir açıdan ele almaktadır; “Spring çalışma zamanı ortamı ve kurumsal uygulamaların işletilmesinde bu ortamın oynadığı kritik rol”. Spring çalışma zamanı kapsamlı bir alan olup, çok çeşitli sayıda uygulama servisini barındırmaktadır. Bu makalede, Spring ile güçlendirilmiş pek çok uygulama tarafından kullanılan çekirdek çalışma zaman bileşenlerine (core runtime componenets),ve Spring ile desteklenmiş bir uygulamanın performans ayarlamasının nasıl yapılacağına odaklanacağız.

Spring çalışma zamanı yüksek düzeyde kalitesi ve rüşdünü doğrudan üretim ortamında ispat etmesi ile haklı bir üne sahiptir. Spring kullanan uygulamaları destekleyen operasyonel ekiplerin yararını gözetmek amacı ile, farzı muhal, çalışma zamanında, bileşenlerde herhangi bir sorun olsa, gözlemlenebilecek belirtileri açıklamaya çalışacağız. Tabi ki, bu tür durumlarla kesinlikle karşılaşmamanızı ümit ederiz!

Spring Çalışma Zamanına Genel Bir Bakış

Spring çalışma zamanı üretim hattındaki uygulamaların işletilmesinde hayati bir rol oynamaktadır. İşin en temel noktasında, Spring çalışma zamanı uygulama bileşenlerinin yaratılmasından, yönetilmesinden, ve bu bileşenler tarafından karşılanan her bir isteğin yürütülmesinden sorumludur. Bu temelin de üstünde, Spring çalışma zamanı kaynaklara erişim, transaction, güvenlik, mesajlaşma vb. pek çok diğer kritik kurumsal servisi yönetmektedir.
Spring çalışma zamanının tam kalbinde “Spring core container” yer almaktadır. Bunun da üzerinde AOP (aspect oriented programming) ve kaynak yükleme (resource loading) ile ilgili çalışma zamanlı bileşenlerin yer alması ile Spring çalışma zamanının çekirdeği (kernel) ortaya çıkmaktadır. Bu çekirdek, uygulama bileşenlerini yaratıp, ayarlarını yapmakta, ve bu bileşenler tarafından karşılanan herbir isteğin yürütülmesini sağlamaktadır.
Spring’in extension mekanizmaları ile doğrudan bu çekirdeğe eklemlenen, örneğin “namespace handler” ve “post-processor”lar da kurumsal uygulamaların ihtiyaç duydukları hayati öneme sahip servisleri sağlayan çalışma zamanlı bileşenlerdir. Bu bileşenler kurumsal servis katmanını oluşturmaktadır. Bu bileşenler, iş mantığı ile ilgili işlemlerin transaction içerisinde yürütülmesi, hataların ele alınması, gelen mesajların uygulama bileşenlerine yönlendirilmesi, güvenlikle ilgili bağlamların (context) vs. oluşturulmasını sağlar.

Şekil 1: Spring çalışma Zamanına Genel Bir Bakış

 

Spring Çalışma Zamanı: Çekirdek (Kernel)

Spring çalışma zamanı, uygulama bileşenlerinin yönetilmesinde temel bir role sahip “core container”, bileşenler üzerindeki operasyonların uyandırılmasını idare eden AOP, kaynak yükleme merkezi, ve olay yönetim alt sisteminden oluşmaktadır.

UYGULAMA BİLEŞENLERİNİN YÖNETİMİ

Spring çekirdeğinin kurumsal bir uygulama başalatılırken ön yüklenmesi (bootstrap) gerekmektedir. Çekirdek, bir kere ayağa kalkıp, çalışır duruma geçtiğinde aşağıdaki ana sorumlulukları yerine getirir:

  • uygulama tarafından ihtiyaç duyulan bileşenlerin tespit edilmesi
  • bu bileşenlerin konfigürasyon bilgilerinin sağlanması
  • bileşenlerin bağımlılık şemasının oluşturulup, yaratılması
  • bir bütün oluşturacak biçimde bileşenlerin birbirlerine monte edilmesi
  • belitilen özellikte bir istek karşılama hattının (request dispatching pipeline) kurulması için bileşenlerin dekorasyonu
  • uygulama bileşenlerinin faaliyet alanlarının ve yaşam döngülerinin yönetilmesi

Bu sorumluluklar aşağıda detaylı biçimde incelenmektedir.

Ön Yükleme (Bootstrapping)

Spring “application context” uygulama için çalışma zamanlı bir bağlam sunar. Uygulama bileşenleri yaratılmadan evvel bir “application context” yaratılmalıdır. Bu bazı uygulamalarda doğrudan programatik olarak yapılmaktadır. Spring kullanan pek çok uygulama için ise Spring’in sağladığı herhangi bir ön yükleme mekanizması yardımı ile otomatik olarak gerçekleşmektedir. Örneğin, bir web uygulamasında Spring’in sağladığı DispatcherServlet başlangıç noktası konumundadır. OSGI tabanlı bir uygulamada ise Spring’in “extender bundle” mekanizması benzer bir görev icra etmektedir.
“Application context” yaratıldığı vakit, uygun bir kaynak yükleme yöntemini kurarak ve Spring “namespace handler”larını tespit edip,kaydederek kendisini çalışma zamanının ortamına uyarlamaktadır. “Namespace handler”lar, “core container”ın XML şema şeklinde tanımlanmış konfigürasyon bilgisini yorumlamasına, bu XML konfigürasyon bilgisindeki eleman ve öznitelik tanımlarının deklare edilmiş uygulama ihtiyaçlarına uygun biçime dönüştürülmesine olanak sağlar.
Ön yükleme fazındaki herhangi bir arıza uygulamayı çalışmaz kılacaktır. Örneğin, bir “namespace handler”ın tespit edilip kaydedilmesindeki bir başarısızlık, eğer uygulama konfigürasyon tanımlarında bu “namespace” içinden XML elemanları kullanılmış ise, “application context”in oluşturulmasında başarısızlık anlamına gelmektedir.

Bileşen Konfigürasyonunun Tespit Edilmesi

Ön yükleme yapıldıktan hemen sonraki kritik aşama, oluşturulması gereken uygulama bileşenlerinin tespit edilmesi, bu bileşenlerin nasıl ayarlanacağı ve ne tür destek servislerine ihtiyaç duyduklarının belirlenmesidir.
Konfigürasyon bilgisi pek çok değişik kaynaktan gelebilir. Bu kaynaklar, ön yükleme aşamasında tespit edilmektedir, ve yine konfigürasyon dosyaları içerisinde belirtilen ilave konfigürasyon metadata kaynakları ile de desteklenmektedir. Örneğin, konfigürasyon bilgisi genellikle birden fazla XML dosyasından sağlanmaktadır. Bu konfigürasyon dosyalarından birisi, Spring 2.5 ile birlikte sağlanan bileşen tarama (component scanning) kabiliyetini aktive edebilir. Bileşen tarama özelliği classpath üzerindeki kaynakları tarayarak ilave konfigürasyon metadata bilgisini tespit etmektedir.
Kayıtlı “namespace handler”lar ve diğer “metadata handler”lar konfigürasyon kaynaklarını işleyerek oluşturulacak altyapı ve uygulama bileşenlerini tespit ederler. Bunların ürettiği, belirtilen uygulama konfigürasyonunu yansıtan “bileşen tanım nesneleri”dir. Uygulama tarafından ihtiyaç duyulan bileşenlerle ilgili bilginin yanında, bu çalışma zamanlı tanım bileşenler arasındaki bağımlılıkları ve konfigürasyon değerlerini de içerir. Kurumsal servis katmanındaki servisler ve uygulama konfigürasyonu da bir veya daha fazla “post processor”ü bileşen tanımları üzerinde doşalmaları, üzerlerinde ilave dönüşümler yapmaları için kaydettirmiş olabilir.
Uygulama başlangıcının bu çok önemli evresi, konfigürasyon bilgileri ile ifade edilmiş uygulama planının (gerekli bileşenler ve onların ayarları) başarılı bir biçimde hafızada yaratılmasını, bir sonraki evrede de konfigüre edilmiş, tam fonksiyonel uygulama nesneleri olarak hayata geçirilmesini garanti eder.
Uygulama planının çıkartılması aşamasında oluşabilecek arızalar, kayıp veya eksik ayarlanmış bileşenlere yol açabilir. Uygulama geliştiriciler tarafından ifade edilen uygulama planının tam olarak oluşturulması bu safhada bir zorunluluktur.

Yaratma, Konfigürasyon ve Montaj

Çekirdeğin çalışma zamanında ki bir sonraki görevi uygulama planını analiz ederek gerekli bileşenleri yaratmaktır. Container bu işlem sırasında bileşenlerin sağlıklı bir sırada yaratılabilmesi için aralarındaki bağımlılıkları ve sıralama kısıtlarını hesaba katmak zorundadır.
Konfigürasyon planı, bileşenlerin ne şekilde oluşturulacağını tanımlamaktadır. Bileşen oluşturma süreci, içine nesnelerin dizin veya servis kütüğünden elde edilmesi, fabrika metodları vasıtası ile yatatılması veya uzak sistem servislerine referans oluşturulması işlemlerini alacak kadar karmaşık bir süreçtir. Yeni bileşenlerin ayarları XML, property dosyaları, placeholder ifadelerinin çevrilmesi gibi yöntemlerle elde edilen konfigürasyon verisi ile gerçekleştirilmektedir. Container String tipindeki verinin, bileşenlerin ihtiyaç duyduğu tiplere çevrilebilmesi, List, Map ve Set gibi yapıları kullanarak konfigürasyon verisinin oluşturulabilmesi için gelişmiş bir desteğe sahiptir. Bir bileşen aynı zamanda işbirliği yaptığı diğer bileşenlerle (bu bileşenin etkileşimde bulunması gereken uygulamanın diğer bileşenleri) beraber yapılandırılmaktadır. En basit manada, işbirlikçi bileşenler (collaborators) isimleri vasıtası ile tespit edilirler, fakat container diğer farklı bir takım yöntemleri, örneğin isim ve tip kriterine bakarak da, bu işbirlikçi bileşenleri tespit edebilir.
Spring uygulama bağlamının (application context) oluşturulması ve ayarlar sırasında ortaya çıkabilecek arızalar, uygulama bağlamının başarılı biçimde ayağa kalkmasını engeller ve sonuç olarak uygulamayı çalışmaz bir halde bırakır. Böyle bir durumda “Exception during initialization” mesajı log dosyalarında belirecektir.

Bileşenlerin Son İşlemeye (Post Processing) Tabi Tutulması

Kurumsal servis katmanındaki servisler, bir veya daha fazla bileşen (bean) son işlemcisini (post processor) “container”a kaydettirmiş olabilir. Bunlara ilave olarak, ayar dosyalarında da kullanıcı düzeyinde son işlemciler tanımlanmış olabilir. Spring çalışma zamanı bu son işlemcileri bir sıraya koyar ve bileşenlerin yaratılmasından sonra bunları sırayla çalıştırır. Kurumsal servis katmanı tarafından kayıt ettirilen pek çok son işlemci bir sonraki bölümde anlatıldığı gibi bileşenleri dekorasyona tabi tutar.

Dekorasyon

Spring bileşenlerin iş mantığındaki çapraz kesen davranışlarını – örneğin transaction namespace’indeki davranışlar, AOP namespace’i ile tanımlanan aspectler veya annotasyonlu sınıflar gibi – tanımlamak için basit bir dekleratif model sunmaktadır. Bu deklerasyonları çalışma zamanında ki ilgili iş mantığını barındıran ara ürünlere (artifact) çeviren mekanizma çok daha karmaşıktır.
Temel yaklaşım ilk once bu bileşen metodları her çağırıldığında çalıştırılması gereken çapraz kesen işlerin olup olmadığını görmek için bu operasyonların tespit edilmesidir. Örneğin, “transaction demarcation” söz konusu mudur? Bir dizi eşleştirme stratejileri mevcuttur, ancak en yaygını bir AspectJ “pointcut” ifadesinin değerlendirilmesidir. Bu işlem ifadenin parse edilmesi ve ilgili metod ile eşleştirilmesini kapsamaktadır. Tespit aşamasından sonra eğer bir veya daha fazla bileşenin metodlarının yine bir veya daha fazla aspect ile advise edilmesi sözkonusu ise, Spring çalışma zamanda ilgili metodlar çağırıldığında (metod çağrısından öce, sonra veya çağrının etrafında) bu advice’ların çalıştırılmasını sağlayacak proxy nesnesini üretir. Bir metod için birden fazla advice’ın çalıştırılması gerekebilir. Bu durumda çekirdeğin sıralama kısıtlarına uyması gerekmektedir. Proxy nesnelerinin üretilmesi ya JDK dinamik proxy ya da çalışma zamanında alt sınıfların yaratılması ile gerçekleştirilir. Container aynı zamanda gerekli davranışın çok daha etkin biçimde üretilmesini sağlayabilen AspectJ load time weaving özelliği ile entegre çalışacak biçimde de ayarlanabilir. Bu durumda uygulama sınıflarının çalışma zamanında byte code düzeyinde ilgili davranışı sergileyecek biçimde değiştirilmesi söz konusudur.
Bazı durumlarda belirtilen bir bileşen metodunun statik yöntemlerle herhangi bir “advise”a tabi tutulup tutulmayacağını tespit etmek mümkün olmayabilir. Böyle bir durumda çekirdek her metod çağrısında yapılacak test işlemini belirlemektedir. Bu test, örneğin, verilen bir argümanın belirli bir tipin nesnesi olup olmadığının kontrolü şeklinde olabilir.
Dekorasyon fazının sonunda artık çapraz kesen servislere ihtiyaç duyan bileşenler için proxy nesneleri oluşturulmuştur, ve gerçek uygulama nesnelerinin yerine bu proxy nesneleri kaydedilmiştir. Dekorasyon fazı kritik kurumsal servislerin, örneğin transaction ve güvenlik yönetimi gibi, temelini oluşturmaktadır.

Bileşenlerin Yaşam Döngüsünün ve Faaliyet Alanlarının Yönetilmesi

Bileşenlerin oluşturulması, ayarlanması, ve dekorasyonu işlemlerinden sonra da container’ın görevi sürmektedir. Bileşenler belli bir yönetime tabi tutulması gereken farklı yaşam döngülerine sahip olabilirler. Örneğin, bir istek boyunca aktif olacak bir bileşen, kendisine erişim söz konusu olan her bir istek sırasında yeniden yaratılmalıdır. Container içindeki bütün singleton olmayan bileşenlerin yaşam döngülerinin sonundaki yıkımlarından da container sorumludur.

İSTEKLERİN SEVKİ (REQUEST DISPATCHING)

Çekirdek, dekorasyona tabi tutulmuş veya belirli bir kapsama alanına sahip bileşenin çalıştırılma hattına ayrıntılı biçimde müdahil olmaktadır. Bu bileşenler üzerinde herhangi bir metod çağırıldığı vakit Spring çalışma zamanı ilk önce, varsa dekorasyon fazında AOP alt sistemi tarafında ayarları yapılmış olan, “interceptor”’leri çalıştırmaktadır. Bu “interceptor”lerden bazılarının çalıştırılması daha evvelden belirlenmiş olan çalışma zamanlı testlere göre gerçekleşebilir. Bu durumda ilk önce bu testler uygulanır, ardından sadece bu testlere göre seçilen “interceptor”ler çalıştırılır. Spring AspectJ’nin “pointcut” ve “advice” modelini “fully typed advice” olarak sağlamaktadır. Bunun anlamı çalışma zamanının elde ettiği bağlamsal bilginin (contextual information) “advice” metodundaki parametrelere bağlamasıdır (bind). Eğer çalıştırılan “advice” işlemin devamına işaret ederse, işte o zaman çalışma zaman hedef bileşeni metod çağırsını gerçekleştirmek için seçer. Basit durumlar için hedefteki bileşen her zaman aynıdır, fakat belirli bir kapsama alanına sahip bileşenler, örneğin “request scoped” bir bileşen için çalışma zaman o andaki aktif alanda yönetilen bileşeni seçer.
Bileşenin metodunun çalışıtılmasından sonraki safhada da AOP çalışma zamanı metod değerini dönmeden evvel “interceptor” zincirini çağırmalıdır. Metodun çalıştırılmasından sonraki bu çağrılarda öncesinde olduğu gibi bir takım çalışma zamanlı testlerin çalıştırılmasını ve parametrelerin bağlanmasını (bind) içermektedir. “Interceptor”lerin çalıştırılması dönen değerlere göre belirli koşullara tabi olabilir veya “exception” fırlatılması sözkonusu olabilir. Dekorasyon fazı AOP alt sisteminin düzgün biçimde ayarlandığını garanti eder, fakat metod çağrılarının çalışma zamandaki yönetimi kurumsal servislerin her seferinde doğru biçimde çalışmalarını sağlar.

KAYNAK YÜKLEME (RESOURCE LOADING)

Kaynak yükleme bileşeni diğer kısımlara göre göreceli olarak küçük olmasına rağmen çekirdeğin (kernel) önemli bir parçasıdır. Bu parça, container her ihtiyaç duyduğunda ilgili kaynakların yüklenmesinden sorumludur. Classpath, dosya sistemi, InputStream, byte array vb yerlerden kaynakların yüklenmesi desteklenmektedir.

OLAYLAR

Çekirdek ayrıca olay (event) yönetim alt sistemine de sahiptir. Bu alt sistem uygulama bağlamında meydana gelen olayların yayımlanması ve herhangi bir uygulama bileşenine bu olayların ulaştırılması ile ilgilenir. Normal şartlarda olayların teslimi senkrondur ve bu teslim olayı yayımlayanın “transaction context”i içerisinde meydana gelir. Bu davranışı isteğe gore özelleştirmek mümkündür.Örneğin, Spring Security bu olay yönetim sistemini güvenlikle ilgili olayların yaratılması ve yayımlanmasında kullanmaktadır. Eğer Spring kümeleme (cluster) yapılmış bir ortamda kullanılıyorsa, kümleme ve kaşe (cache) alanındaki pek çok üretici, uygulama bağlamı ile ilgili olayların küme genelindeki bütün sistemlere dağıtılmasını desteklemektedir.

Spring WebFlow 1.0’dan 2.0’a Geçiş

Bir süredir projelerimizde Spring WebFlow 1.0.x’i kullanmaktaydık. 1.0.x’i kullanmaya başladığımız dönemlerde Spring WebFlow ekibi de 2.0.x için hummalı bir çalışma içine girmesine rağmen 2.0.x’in ilk dönemleri üretim hattındaki uygulamaların ihtyaçlarını karşılayacak kalitede değildi, milestone sürümleri arasında ciddi mimarisel ve APIsel değişiklikler meydana geliyordu. Bu nedenle 1.0.x’den başlamayı uygun gördük.

Ancak şu an için Spring WebFlow, 2.0.4 sürümü ile istenen olgunluğa ulaşmış durumda. 2.0.x ile birlikte webflow kullanan uygulamalar için önemli gelişmeler sağlanmaktadır. Örneğin Hibernate kullanan web uygulamaları için LazyException problemine kökten bir çözüm getiriliyor. Flow, state ve transition düzeyinde güvenlik kabiliyeti sağlanıyor. Bunun yanı sıra JSF kullanan web uygulamaları MVC pattern’ı açısından daha sağlıklı bir yapıya kavuşuyor. Yine webflow’un JSF ile birlikte kullanılabilirliği noktasında pek çok yeni özellik sunuluyor.

Bizde eski projelerimizde olmasa da yeni projelerimizde bundan böyle Spring WebFlow 2.0.x ile devam etmeye karar verdik. Bunun için de 1.0.x’den 2.0.x’e geçiş aşamasında bütün projelerimiz tarafından ortak kullanılan middleware çözümlerimizde bazı değişikliklere gidildi. Bu geçiş süreci ile ilgili öne çıkan ve dikkat edilmesi gereken noktaları ayrıntılı biçimde kayıt altına almaya çalıştım. Bu çalışmanın 2.0.x, 1.0.x’e göre hangi noktalarda farklılaşıyor, yeni özellikleri hakkında bilinmesi gereken püf noktalar gibi konularda hem Spring WebFlow 1.0.x’den 2.0.x’e geçmeye çalışanlar için hem de Spring WebFlow 2.0.x kullanmayı düşünenler için yararlı olacağını düşünüyorum.

İlgili dokümana buradan erişebilirsiniz.

A Hybrid SessionFactoryBean

LocalSessionFactoryBean is a nice way to create and initialize a Hibernate SessionFactory instance in applications. It lets us to configure other objects and properties, such as DataSource, and other hibernate properties that SessionFactory instance depends during initialization inside Spring?s context mechanism. We can easily configure other parts of our application, that are related with SessionFactory instance in that way,too. For example, we currently have an audit logging mechanism based on Hibernate?s entity interceptor, and this part of our application has to be brought up inside Spring context.

There might be cases, where SessionFactory must be shared accross multiple applications. In this case, we can wire up one via JndiObjectFactoryBean, starting up SessionFactory instance via a separate mechanism outside applications and register it in a JNDI context as a server-wide resource in a J2EE application server environment.

We are in a similar situation as mentioned above, we have many modules each runs as a separate web application in our system and each one needs a SessionFactory instance. SessionFactory instances are identical accross all modules, ie. each one loads up same Hibernate mapping files, connects to same database, and employs same properties. Hence, it would be good enough that one of those modules could bring a SessionFactory instance up and make it available to all other modules. That way, we might get at least a performance improvement during our modules? startup period.

We needed a way to start a SessionFactory instance up inside Spring context and then register it as a server-wide resource in JNDI context in the application server environment. As a result, we merged functionalities of both LocalSessionFactoryBean and JndiObjectFactoryBean classes to get our work to be done, and named it as JndiEnabledLocalSessionFactoryBean.

It extends from LocalSessionFactoryBean, and add some additional properties to enable JNDI lookup, namely jndiName, jndiAccessor, enableJndiLookup. If enableJndiLookup is true, it first tries to load SessionFactory instance from specified JNDI location. If there is an available instance found, it is provided to our application, otherwise it intializes a SessionFactory instance and registers it to that JNDI location. That way, other modules will get rid of intializing a new instance again and again separately. If enableJndiLookup property is false, it behaves as an ordinary LocalSessionFactoryBean instance, which might be preferrable for a development environment.

Here is an excerpt from source code and bean defintion from Spring context file as follows;

public class JndiEnabledLocalSessionFactoryBean extends LocalSessionFactoryBean {

    private Object sessionFactoryObject;
    private String jndiName;
    private JndiAccessor jndiAccessor;
    private boolean enableJndiLookup;

    public void afterPropertiesSet() throws IllegalArgumentException, HibernateException, IOException {
        try {
            if(isEnableJndiLookup()) {
              sessionFactoryObject = getJndiAccessor().getJndiTemplate().lookup(getJndiName());
            } else {
                initializeSessionFactory();
            }
        } catch (NamingException e) {
            initializeSessionFactory();
            try {
                 getJndiAccessor().getJndiTemplate().bind(getJndiName(),super.getObject());
            } catch (NamingException e1) {
                logger.warn("Cannot bind SessionFactory to JNDI Context",e1);
            }
        }
    }

    private void initializeSessionFactory() throws HibernateException, IOException {
        super.afterPropertiesSet();
        sessionFactoryObject = super.getObject();
    }
    public void destroy() throws HibernateException {
        if(isEnableJndiLookup()) {
               try {
                   getJndiAccessor().getJndiTemplate().unbind(getJndiName());
               } catch (NamingException e) {
                   logger.warn("Cannot unbind SessionFactory from JNDI Context",e);
               }

        }
        super.destroy();
    }

    public Object getObject() {
        return sessionFactoryObject;
    }

    //getter and setter methods for properties defined above, jndiName, jndiAccessor, enableJndiLookup
}

<bean id="sessionFactory" class="tbs.verisozlugu.ambar.JndiEnabledLocalSessionFactoryBean">
      <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
      </property>
      <property name="dataSource">
            <ref local="dataSource" />
      </property>
      <property name="jndiName">
            <value>vsSessionFactory</value>
      </property>
      <property name="enableJndiLookup">
            <value>false</value>
      </property>
      <property name="jndiAccessor">
            <ref local="jndiAccessor"/>
      </property>
</bean>

<bean id="jndiAccessor" class="org.springframework.jndi.JndiAccessor">
      <property name="jndiEnvironment">
            <value>
      java.naming.factory.initial=org.exolab.jms.jndi.InitialContextFactory
      java.naming.provider.url=rmi://localhost:1099
            </value>
      </property>
</bean>

Java mı Yoksa .NET mi?

Yada neden Java? Benzer sorularla sık sık karşılaşıyorum. Kişisel olarak hangi platforma yönelmeleri gerektiğini öğrenmek isteyen öğrenci ve yeni mezun arkadaşların yanı sıra, kurumsal olarak da hangi platforma yatırım yapmalarını öğrenmek isteyen yöneticilerle de sık sık konuşuyorum. Gerçekten cevaplanması zor bir soru ve herkes için geçerli tek bir cevabın da olduğunu sanmıyorum.

Üzerinde düşünülmesi gereken pek çok parametre var. Geliştirme ve runtime ortamı, geliştiricilerin profilleri, sektördeki nitelikli personel düzeyi, yazılım geliştirme ve bakım maliyetleri, upgrade imkanları, geriye yönelik uyumluluklar, her iki ekosistemin de geliştiricilere ve kuruma işini yapmada ne kadar yardımcı olduğu, ne tür imkanlar veya araçlar sunduğu, platformun etrafındaki topluluklar ve aktiviteler, platformun arkasındaki kurumsal güçler, hatta ülkenin dış politikası gibi onlarca kriter üzerinden değerlendirme yapmak gerekiyor.

Bir platformu tercih ettikten sonra diğerine geçişin de pek mümkün olduğunu düşünmüyorum. Bütün teknolojisini Java etrafında şekillendirmiş, uygulamalarını Java ile geliştiren, Java geliştiricilere sahip bir organizasyonun hemen yarın ben artık .NET ile çalışacağım demesi imkansız. Bunun tersi de geçerli. Bireysel bazda da benzer bir durum söz konusu…Java teknolojilerine yatırım yapan, kendisini bu alanda geliştiren bir mühendisin belirli bir bilgi birikimi ve deneyime sahip olması için hatrı sayılır bir süreye ihtiyacı var. Bu noktada .NET platformuna geçiş yapabilir mi, bir süre sonra oraya da uyum sağlayabilir mi? Evet yapabilir, ancak pratikte bu tür geçişlerin veya gidiş gelişlerin fani ömürlü bizler için çok da kolay olduğunu söyleyemeyiz.

Kurumsal olarak her iki platforma da yatırım yapmak söz konusu olabilir, ancak bunun da kuruma oldukça tuzlu olacağını, bunun yanı sıra kurumun verimliliği açısından da çok sağlıklı sonuçlar vermeyeceğini söyleyebilirim. Ancak kurumun kendi ekosistemini mümkün olduğunca farklı teknolojilerin birbirleri ile uyumlu çalışabileceği şekilde oluşturması da önemli bir stratejidir. Yarın ne olacak kimse bilemez. Oracle’ın Java için iyi veya kötü olacağını söyleyebiliyor muyuz? Ya da kendimizi Microsoft’a emanet edebilir miyiz? Elbette hayır.

Bu durumda kurumsal olarak bu iki platform ve uğraştığımız işlerle ilgili olarak diğer teknolojilerin de resmin içerisine girebileceği bir “kurumsal mimari” doğru bir yaklaşım olabilir. Hiçbir teknolojik yatırım sizi 5 sene, 10 sene sonrasına taşımayacaktır. Süreç içerisinde kendinizi sürekli olarak yenilemek ve geliştirmek zorundasınız. Böyle bir dinamik ortamda mimarinizin veya ekosisteminizin de dönüşebilir olması gerçekten önem kazanıyor. Aslında geldiğimiz noktada kurum bazında soru şu şekilde de sorulabilir. Java mı yoksa .NET mi daha devingen ve dönüşebilir bir ekosistem oluşturmamızı sağlayabilir?