Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Saturday, June 25, 2011

ActiveMQ messages database logging with Apache Camel

Hi, we had epayment system based on Fuse ActiveMQ and  we decided to log into DB all messages passed through ActiveMQ. I want to share my first experience with Apache Camel :)

I changed ActiveMQ configuration and used ActiveMQ feature: 'Mirrored Queues' to forward all messages to mirrored queues prefixed with qmirror. Here is ActiceMQ configuration:

<destinationInterceptors>
<mirroredQueue copyMessage = "true" postfix="" prefix="qmirror."/>
</destinationInterceptors>

So now when we have copied messages in queues with names: 'qmirror.*' and it is time to log them with Apache Camel. Thus I changed '$ACTIVEMQ_HOME/conf/camel.xml' config file in the following way:

<beans...>

    <context:component-scan base-package="info.sargis.dbloggger"/>

    <context:annotation-config/>

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <package>info.sargis.dbloggger</package>
    </camelContext>

    <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"/>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:TWMDB"/>
        <property name="username" value="activemq"/>
        <property name="password" value="ee0thaXu"/>
        <property name="maxActive" value="5"/>
        <property name="maxIdle" value="2"/>
    </bean>

    <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
        <property name="connectionFactory">
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL"
                          value="vm://localhost?create=false&amp;waitForStart=10000&amp;broker.populateJMSXUserID=true"/>
                <property name="userName" value="${activemq.username}"/>
                <property name="password" value="${activemq.password}"/>
            </bean>
        </property>
    </bean>

</beans>

and created project with structure:

|-- docs
|   `-- create_db.sql
|-- payment-dblogger.iml
|-- pom.xml
|-- README.txt
`-- src
    |-- data
    `-- main
        |-- java
        |   `-- info
        |       `-- sargis
        |           `-- dbloggger
        |               |-- DBLoggerRouteBuilder.java
        |               `-- logger
        |                   |-- DBLogger.java
        |                   |-- DBLoggerProcessor.java
        |                   `-- Logger.java
        `-- resources
            |-- log4j.properties
            `-- META-INF
                `-- spring
                    `-- camel-context.xml

note that project artifact payment-dblogger-*.jar should be deployed to '$ACTIVEMQ_HOME/webapps/camel/WEB-INF/lib'

Now its time for router :) here everything is simple as well:

package info.sargis.dbloggger;

import org.apache.camel.builder.RouteBuilder;

public class DBLoggerRouteBuilder extends RouteBuilder {
    @Override
    public void configure() {
        from("activemq:topic:qmirror.>").threads().processRef("dbLoggerProcessor");
    }
}

Friday, April 1, 2011

Fun with 'fluent interface' and Java

Here is my story about 'fluent interface' and Java. In one project I have to compare in many places BigDecimals and other Comparable objects. So if I have:

BigDecimal requestedAmount = ...
BigDecimal paymentAmount = ...

In Java to compare BigDecimals I should use

if(requestedAmount.compareTo(paymentAmount) == 0) {

}

For me its not natural to compare numbers in such way and I decided to try to write small technical DSL, and here how looks my code now:

import static com.webbfontaine.twm.accounting.epaylog.utils.CompareDSL.*;

if (eq(valueOf(paymentAmount).comparingWith(requestedAmount))) {
}

if (ne(valueOf(paymentAmount).comparingWith(requestedAmount))) {
}

if (gt(valueOf(paymentAmount).comparingWith(requestedAmount))) {
}
.... and etc.

and here is API/DSL code:

public class CompareDSL {

    public static <T extends Comparable<T>> PairToCompare<T> valueOf(T value) {
        return new PairToCompare<T>(value);
    }

    public static <T extends Comparable<T>> boolean eq(PairToCompare<T> pairToCompare) {
        return pairToCompare.firstValue.compareTo(pairToCompare.secondValue) == 0;
    }

    public static <T extends Comparable<T>> boolean ne(PairToCompare<T> pairToCompare) {
        return !eq(pairToCompare);
    }

    public static <T extends Comparable<T>> boolean gt(PairToCompare<T> pairToCompare) {
        return pairToCompare.firstValue.compareTo(pairToCompare.secondValue) > 0;
    }

    public static <T extends Comparable<T>> boolean ge(PairToCompare<T> pairToCompare) {
        return pairToCompare.firstValue.compareTo(pairToCompare.secondValue) >= 0;
    }

    public static <T extends Comparable<T>> boolean lt(PairToCompare<T> pairToCompare) {
        return pairToCompare.firstValue.compareTo(pairToCompare.secondValue) < 0;
    }

    public static <T extends Comparable<T>> boolean le(PairToCompare<T> pairToCompare) {
        return pairToCompare.firstValue.compareTo(pairToCompare.secondValue) <= 0;
    }

    public static class PairToCompare<T extends Comparable<T>> {

        private T firstValue;
        private T secondValue;

        public PairToCompare(T firstValue) {
            this.firstValue = firstValue;
        }

        public PairToCompare<T> comparingWith(T value) {
            this.secondValue = value;
            return this;
        }

    }
}

API of course not the best one, but for me it works fine and it was fun to write it :-)

Sunday, October 17, 2010

How to configure Oracle/SUN JVM to send notification in case of JVM fatal error/crash

Recently we had JVM crash in production server and of course we had notified by some monitoring tools but not as fast as we could expect. So I configured our server with following JVM option:
-XX:OnError="sendjvmcrashsms". 'sendjvmcrashsms' is a Linux shell/bash file which is of course in user PATH, and here is example of the file

#! /bin/bash
echo "Production JVM crashed" | mail -s "+33000000000"


It will send mail to address with subject '+33000000000'. In our server we have configured SMS gateway which will send SMS to number(s) defined in subject, and here is docs:
-XX:OnError="<cmd args>;<cmd args>" - Run user-defined commands on fatal error

Sunday, October 10, 2010

Experience with slf4j/logback markers

I want to share my experience with slf4j/logback markers, hope can be useful for others.

Problem: want to have possibility to mail for particular log messages regardless log level.

Solution: Using slf4j/logback markers to mark :) interested messages and logback Filter.

First I've defined simple class to keep like global registry for slf4j markers:

public class LOGMarkers {
      public static final Marker SEND_MAIL_MARKER = MarkerFactory.getMarker("SEND_MAIL");
}

and here is code example which is actually using marker:

LOGGER.info(LOGMarkers.SEND_MAIL_MARKER, "Cannot save document with id: {}", instanceId);

or

try {
  .................
} catch (SomeException e) {
     LOGGER.warn(LOGMarkers.SEND_MAIL_MARKER, "", e);
}

also I have to write logback filters and use it in logback configuration file:

package info.sargis.logging.filter;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.AbstractMatcherFilter;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

/**
* Created by IntelliJ IDEA.
* User: Sargis Harutyunyan
* Date: 10 oct. 2010
* Time: 20:12:12
*/
public class MarkerFilter extends AbstractMatcherFilter {

Marker markerToMatch;

public void start() {
     if (this.markerToMatch != null) {
     
     super.start();
     } else {
     
     addError(String.format("The marker property must be set for [%s]", getName()));
     }
}

public FilterReply decide(ILoggingEvent event) {
     Marker marker = event.getMarker();
     if (!isStarted()) {
     
     return FilterReply.NEUTRAL;
     }

     if (marker == null) {
     
     return onMismatch;
     }

     if (markerToMatch.contains(marker)) {
     
     return onMatch;
     }
     return onMismatch;
}

public void setMarker(String markerStr) {
     if (markerStr != null) {
     
     markerToMatch = MarkerFactory.getMarker(markerStr);
     }
}


}

 and finally logback config file example:


    <appender name="MARKER_EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
        <filter class="info.sargis.logging.filter.MarkerFilter">
            <marker>SEND_MAIL</marker>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
            <marker>SEND_MAIL</marker>
        </evaluator>
        <SMTPHost>localhost</SMTPHost>
        <To>sargis@localhost</To>
        <From>twm@localhost</From>
        <Subject>EPAYMAIL: %date %-5level - %message</Subject>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%date [%thread] %-5level U:%X{vaspId} - %message%n</Pattern>
        </layout>
    </appender>

    <logger name="info.sargis" level="INFO">
        <appender-ref ref="EPAYCONSOLE"/>
        <appender-ref ref="MARKER_EMAIL"/>
    </logger>

and voila :)

Sunday, March 28, 2010

How to config Java WebStart to use logging subsystem

I am using for client side slf4j as facade framework but behind we decided to use jdk14 standard logging. SO to config jdk14 for webstart you need to
do following:

1) Define according your OS platform JAVAWS_VM_ARGS environment variable, for linux its like this:
 export JAVAWS_VM_ARGS="-Djava.util.logging.config.file=/home/sargis/<localpath>/logging.properties"
2) Here is logging.properties content:

handlers= java.util.logging.ConsoleHandler

java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

.level= INFO

info.sargis.level = FINE
info.sargis.handlers = java.util.logging.ConsoleHandler