Sansür, bir toplumun kendine olan güvensizliğini yansıtır ve otoriter rejimlerin belirgin bir özelliğidir.

--Potter Stewart

16.02.2011

JSF'de SMTPAppender ile Gmail üzerinden Exception Bildirimi

Geliştirdiğimiz uygulamaların kullanımı esnasında oluşabilecek kullanıcı kaynaklı ya da sistemsel hataları yakalayıp kullanıcıyı bilgilendirmek ya da yönlendirmek için yoğun çaba harcarız. Harcanan çabaya rağmen gözden kaçırılan bir hatanın oluşması durumunda ise hatanın sebebini bulmak için genelde loglar kurcalanır. Bir hata oluştuğunu anlamak için de ya kullanıcı geri bildirimi ya da rutin log kontrolleri yapılır. Bu geri bildirimin hata oluştuğu esnada bir ileti ile otomatik olarak yapılması da hataya müdahale için iyi bir yöntemdir. Bunun için popüler loglama kütüphanesi log4j'nin SMTPAppender sınıfı kullanılabilir. Örneğin yakalanan bir Exception'ın error seviyesinde loglanması ile log4j'nin bir ileti göndermesi sağlanabilir.

Bu iş için Gmail'i kullanabilir miyiz diye küçük bir arama yaptığımda şu blog girdisine rastladım. Örnek kodta Gmail ayarları barındıran bir Enum mevcut. Mail gönderimi için de Spring çatısının Email desteği kullanılmış. Spring Email kullanabilmek için ayrıca activation.jar ve mail.jar kütüphaneleri de gerekli. Madem Spring kullanacağız bağımlılık yönetimini Spring'e bırakalım. Enum sınıfı yerine öncelikle JavaMailSenderImpl yaratan bir bean tanımı yapalım:


<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="smtp.googlemail.com"/>
        <property name="port" value="587"/>
        <property name="username" value="gmailAccount@gmail.com"/>
        <property name="password" value="armut123"/>
        <property name="javaMailProperties">
            <props>
                <prop key="mail.smtp.auth">true</prop>                
                <prop key="mail.smtp.starttls.enable">true</prop>                
            </props>
        </property>
    </bean>

Yapmış olduğumuz bean tanımını ApplicationContext'ten alıp GMailAppender sınıfının kullanımına sunacak olan JavaMailSenderFactory sınıfını yazalım:

@Component
public class JavaMailSenderFactory implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    public static JavaMailSender createJavaMailSender(){
        return applicationContext.getBean("javaMailSender", JavaMailSender.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

JavaMailSenderFactory sınıfını da annotation yardımıyla Spring bean olarak tanımladık. Bean tanımını XML ile de yapabilirdik. Bu sınıf ApplicationContextAware arayüzünü gerçekleştirdiği için Spring application context'e erişerek JavaMailSender beanine ulaşabiliriz. Bunun için bean tanımını döndüren static bir metot yeterli olur. Log4j'nin SMTPAppender sınıfınından türetilen GMailAppender sınıfının bizim yaptığımız değişiklikler sonrasında aldığı hal şu şekilde:

public class GMailAppender extends SMTPAppender {
    private static final String NL = System.getProperty("line.separator");
    
    @Override
    public void append(LoggingEvent event){
        
        SimpleMailMessage msg = new SimpleMailMessage();
        StringBuilder builder = new StringBuilder();
        builder.append(getLayout().format(event));
        builder.append(event.getMessage().toString());
        if (event.getThrowableInformation() != null) {
            builder.append(NL);
            String[] stackTrace = event.getThrowableInformation().getThrowableStrRep();
            for(int i = 0; i < stackTrace.length; i++) {
                builder.append(stackTrace[i] + NL);
            }
        }
        String[] senders = getTo().trim().replace(" ", "").split(",");
        
        msg.setTo(senders);
        msg.setText(builder.toString());
        msg.setSubject(this.getSubject());
        
        try{
            JavaMailSender javaMail = JavaMailSenderFactory.createJavaMailSender();
            javaMail.send(msg);
        }catch (MailException e) {
            System.err.println("!!! HATA MAILI GONDERILEMEDI !!! : ");
            e.printStackTrace();
        }
    }
}

Referans aldığım sitedeki append()metodunu da az biraz değiştirdim. Değişikliğin nedeni append() metodunun super.append(event) ile akışı SMTPAppender sınıfına aktarmış olması. GMailAppender içinde gerekli biçimlendirme ve gönderim işlemi yapıldığı için buna gerek yok aslında. Şimdi de GMailAppender için Log4J ayarlarını yapılandıralım. Ben properties dosyası yerine log4j.xml kullanmayı tercih ettim:

<root>
        <priority value="info" />
        <appender-ref ref="GMailAppender" />
    </root>

    <appender name="GMailAppender" class="com.prime.mobileme.core.util.appender.GMailAppender">
        <param name="BufferSize" value="1" />
        <param name="SMTPDebug" value="false" />
        <param name="To" value="dasgin@gmail.com" />
        <param name="Subject" value="Ucundan Java ERROR" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"    value="[%d] [%t] %-5p %c %x" />
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="error" />
            <param name="LevelMax" value="fatal" />
        </filter>
    </appender>


Yukarıdaki ayarlardan da anlaşılacağı gibi yakaladığınız Exception'ları log4j ile error ya da fatal olarak loglarsanız loglanan hata mail olarak belirttiğiniz Gmail hesabı üzerinden gönderilecektir. Peki yakalayamadığımız hata durumlarında ne yapmalı? Eğer JSF 2 kullanıyorsanız şu blog girdisinden faydalanabiliriz. Gene buradaki örneği kendi ihtiyaçlarımıza göre yeniden düzenleyelim. Öncelikle oluşan hatayı ele alarak log4j'ye error olarak log düşen ExceptionHandler sınıfını hazırlayalım:

public class MyExceptionHandler extends ExceptionHandlerWrapper {

    private static Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);
    private ExceptionHandler wrappedExceptionHandler;
    
    public MyExceptionHandler(ExceptionHandler exceptionHandler){
        this.wrappedExceptionHandler = exceptionHandler;
    }
    
    @Override
    public ExceptionHandler getWrapped() {
        return wrappedExceptionHandler;
    }
    
    @Override
    public void handle(){
        
        for (ExceptionQueuedEvent event : getUnhandledExceptionQueuedEvents()) {
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext)event.getSource();
            Throwable t = context.getException();
            logger.error("Unhandled Exception: ", t);
        }
        getWrapped().handle();

    }

}

Ardından ExceptionHandler sınıfımızı JSF'in kullanabilmesi için ExceptionHandlerFactory sınımızı hazırlayalım:

public class MyExceptionHandlerFactory extends ExceptionHandlerFactory {

    private ExceptionHandlerFactory parent;
    
    public MyExceptionHandlerFactory(ExceptionHandlerFactory parent){
        this.parent = parent;
    }
    
    @Override
    public ExceptionHandler getExceptionHandler() {
        return new MyExceptionHandler(parent.getExceptionHandler());
    }

}

En son adımda da faces-config.xml'e yazdığımız factory sınıfını tanıtmak kalıyor:

<factory>
          <exception-handler-factory>
            ucundan.java.MyExceptionHandlerFactory
          </exception-handler-factory>
    </factory>


Kaynaklar:
GMail and log4j e-mail appender - error STARTTLS
JSF 2 Exception Handling

Hiç yorum yok:

Yorum Gönder