Posts tagged: spring
Almost every Spring developer already seen and used @Transactional annotation, responsible for the database transaction demarcation.
It creates an AOP around advice, that starts the database tranaction before method execution and commits (or rollbacks) it, after the execution ends.
This post demonstrates, how to create a custom annotation working like @Transactional and responsible for profling method execution. It uses method intercepting capabilities provided by the Spring AOP, however without utilizing AspectJ.
1. We’ll start with the custom annotation @ProfileExecution, that will mark bean methods we want to profile:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited @Documented public @interface ProfileExecution { }
Typical Spring’s message source (see the MessageSource Javadoc) relies on the resource bundles. However it is quite easy to create a custom MessageSource implementation that determines messages from some other external data source.
This post demonstrates how to initialize a database-driven MessageSource and presents two approaches how messages can be saved in the database.
The most common usecase to configure Spring’s application context using external properties is a combination of a property file and a PropertyPlaceholderConfigurer bean. If you deploy your application in multiple environments (e.g. development, pre-production, production..) it is a good idea to externalize this configuration file and to bring the application to load this file from external location on startup.
This post demonstrates how to replace the property file by other external data source (especially database) and make it possible to configure a Spring based web application on startup from it.
There are already several blog articles on the web about this topic. In this post I want to demonstrate how you can process JMS messages that have been created based on the Oracle Custom Types.
This is also the solution to the common problem: JMS-137: Payload factory must be specified for destinations with ADT payloads.
In the run-up to the post please find below some links how to connect Spring via JMS to the Oracle Advanced Queing:
Assuming following custom oracle data type, queue table and the queue itself were defined in the database:
create or replace type aq_event_obj as object ( datetime date, id varchar2(100), payload varchar2(255) );
sys.dbms_aqadm.create_queue_table(queue_table => 'T_AQ_EVENT', queue_payload_type => 'AQ_EVENT_OBJ', sort_list => 'PRIORITY, ENQUEUE_TIME', compatible => '10.0.0', primary_instance => 0, secondary_instance => 0);
sys.dbms_aqadm.create_queue(queue_name => 'Q_AQ_EVENT', queue_table => 'T_AQ_EVENT', queue_type => sys.dbms_aqadm.normal_queue, max_retries => 5, retry_delay => 0, retention_time => 0);
First step is a standard procedure creating JMS Connection Factory, Event Queue and JMS Template in Spring described in the posts above (see links). Just a brief overview for this here:
<bean id="jmsQueueConnectionFactory" class="oracle.jms.AQjmsFactory" factory-method="getQueueConnectionFactory"> <constructor-arg index="0" ref="dataSource" /> </bean> <bean id="aqEventQueue" class="net.javaforge.blog.oracle.aq.OracleAqQueueFactoryBean"> <property name="connectionFactory" ref="jmsQueueConnectionFactory" /> <property name="oracleQueueName" value="Q_AQ_EVENT" /> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="jmsQueueConnectionFactory" /> <property name="defaultDestination" ref="aqEventQueue" /> </bean>
And the corresponding java classes:
public abstract class AbstractOracleAqFactoryBean { private ConnectionFactory connectionFactory; private String oracleQueueName = null; private String oracleQueueUser = null; @Required public void setConnectionFactory(final ConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } public String getOracleQueueName() { return oracleQueueName; } @Required public void setOracleQueueName(final String oracleQueueName) { this.oracleQueueName = oracleQueueName; } public String getOracleQueueUser() { return oracleQueueUser; } public void setOracleQueueUser(final String oracleQueueUser) { this.oracleQueueUser = oracleQueueUser; } public boolean isSingleton() { return false; } protected AQjmsSession getSession() throws JMSException { final AQjmsSession session = (AQjmsSession) connectionFactory .createConnection().createSession(true, Session.SESSION_TRANSACTED); return session; } } public class OracleAqQueueFactoryBean extends AbstractOracleAqFactoryBean implements FactoryBean { public Class getObjectType() { return javax.jms.Queue.class; } public Queue getObject() throws JMSException { final AQjmsSession session = getSession(); return session.getQueue(getOracleQueueUser(), getOracleQueueName()); } }
Furthermore as soon as we want to process custom oracle data type as message payload, we have to implement the ORADataFactory interface and describe our aq_event_obj type in it.
This interface can be implemented by hand or you can just generate an appropriate implementation with Oracle’s JDeveloper IDE. To save time i have chosen the second option and generated following ugly but fully functional class:
public class AQCustomPayloadObject implements ORAData, ORADataFactory { public static final String _SQL_NAME = "AQ_EVENT_OBJ"; public static final int _SQL_TYPECODE = OracleTypes.STRUCT; protected MutableStruct _struct; protected static int[] _sqlType = { 91, 12, 12 }; protected static ORADataFactory[] _factory = new ORADataFactory[3]; protected static final AQCustomPayloadObject _AqEventObjFactory = new AQCustomPayloadObject(); public static ORADataFactory getORADataFactory() { return _AqEventObjFactory; } /* constructors */ protected void _init_struct(boolean init) { if (init) _struct = new MutableStruct(new Object[3], _sqlType, _factory); } public AQCustomPayloadObject() { _init_struct(true); } public AQCustomPayloadObject(java.sql.Timestamp datetime, String id, String payload) throws SQLException { _init_struct(true); setDatetime(datetime); setId(id); setPayload(payload); } /* ORAData interface */ public Datum toDatum(Connection c) throws SQLException { return _struct.toDatum(c, _SQL_NAME); } /* ORADataFactory interface */ public ORAData create(Datum d, int sqlType) throws SQLException { return create(null, d, sqlType); } protected ORAData create(AQCustomPayloadObject o, Datum d, int sqlType) throws SQLException { if (d == null) return null; if (o == null) o = new AQCustomPayloadObject(); o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory); return o; } /* accessor methods */ public java.sql.Timestamp getDatetime() throws SQLException { return (java.sql.Timestamp) _struct.getAttribute(0); } public void setDatetime(java.sql.Timestamp datetime) throws SQLException { _struct.setAttribute(0, datetime); } public String getId() throws SQLException { return (String) _struct.getAttribute(1); } public void setId(String id) throws SQLException { _struct.setAttribute(1, id); } public String getPayload() throws SQLException { return (String) _struct.getAttribute(2); } public void setPayload(String payload) throws SQLException { _struct.setAttribute(2, payload); } }
Now it is time for the magic. We have to override Spring’s default DefaultMessageListenerContainer class by creating our special message consumer that can deal with the aq_event_obj type :
public class AQCustomPayloadMessageListenerContainer extends DefaultMessageListenerContainer { @Override protected MessageConsumer createConsumer(Session session, Destination destination) throws JMSException { return ((AQjmsSession) session).createConsumer(destination, getMessageSelector(), AQCustomPayloadObject.getORADataFactory(), null, isPubSubNoLocal()); } }
And the corresponding Spring’s XML configuration:
<bean id="jmsContainer" class="net.javaforge.blog.oracle.aq.AQCustomPayloadMessageListenerContainer"> <property name="connectionFactory" ref="jmsQueueConnectionFactory" /> <property name="destination" ref="aqEventQueue" /> <property name="messageListener" ref="aq.receiver" /> <property name="sessionTransacted" value="true" /> <property name="transactionManager" ref="tx.manager" /> </bean>
The only task now is to implement the message receiver:
@Named("aq.receiver") public class AQReceiver implements MessageListener { private static final Logger logger = LoggerFactory .getLogger(AQReceiver.class); @Override public void onMessage(Message message) { try { AQjmsAdtMessage msg = (AQjmsAdtMessage) message; AQCustomPayloadObject obj = (AQCustomPayloadObject) msg.getAdtPayload(); logger.info("Id: {}", obj.getid()); logger.info("Datetime: {}", obj.getDatetime()); logger.info("Payload: {}", obj.getPayload()); } catch (Exception e) { logger.error("Error receiving queue message!", e); } } }