All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kaleidofoundry.messaging.rdv.RdvConsumer Maven / Gradle / Ivy

/*  
 * Copyright 2008-2021 the original author or authors 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kaleidofoundry.messaging.rdv;

import static org.kaleidofoundry.messaging.ClientContextBuilder.CONSUMER_RECEIVE_TIMEOUT_PROPERTY;
import static org.kaleidofoundry.messaging.ClientContextBuilder.RDV_SUBJECTS;
import static org.kaleidofoundry.messaging.ClientContextBuilder.TRANSPORT_REF;
import static org.kaleidofoundry.messaging.MessagingConstants.MESSAGE_BODY_BYTES_FIELD;
import static org.kaleidofoundry.messaging.MessagingConstants.MESSAGE_BODY_TEXT_FIELD;
import static org.kaleidofoundry.messaging.MessagingConstants.MESSAGE_ID_FIELD;
import static org.kaleidofoundry.messaging.MessagingConstants.MESSAGE_TYPE_FIELD;
import static org.kaleidofoundry.messaging.MessagingConstants.MessagingMessageBundle;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.kaleidofoundry.core.context.EmptyContextParameterException;
import org.kaleidofoundry.core.context.IllegalContextParameterException;
import org.kaleidofoundry.core.context.RuntimeContext;
import org.kaleidofoundry.core.plugin.Declare;
import org.kaleidofoundry.core.util.StringHelper;
import org.kaleidofoundry.messaging.AbstractConsumer;
import org.kaleidofoundry.messaging.AbstractMessage;
import org.kaleidofoundry.messaging.BytesMessage;
import org.kaleidofoundry.messaging.Consumer;
import org.kaleidofoundry.messaging.JavaBeanMessage;
import org.kaleidofoundry.messaging.Message;
import org.kaleidofoundry.messaging.MessageException;
import org.kaleidofoundry.messaging.MessageTypeEnum;
import org.kaleidofoundry.messaging.MessagingConstants;
import org.kaleidofoundry.messaging.MessagingException;
import org.kaleidofoundry.messaging.TextMessage;
import org.kaleidofoundry.messaging.TransportException;
import org.kaleidofoundry.messaging.XmlMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.tibco.tibrv.Tibrv;
import com.tibco.tibrv.TibrvCmListener;
import com.tibco.tibrv.TibrvCmMsg;
import com.tibco.tibrv.TibrvDispatcher;
import com.tibco.tibrv.TibrvException;
import com.tibco.tibrv.TibrvListener;
import com.tibco.tibrv.TibrvMsg;
import com.tibco.tibrv.TibrvMsgCallback;
import com.tibco.tibrv.TibrvMsgField;
import com.tibco.tibrv.TibrvQueue;

/**
 * Consumer for Tibco RDV
 * 
 * @author jraduget
 */
@Declare(MessagingConstants.RDV_CONSUMER_PLUGIN)
public class RdvConsumer extends AbstractConsumer implements Consumer {

   static final Logger LOGGER = LoggerFactory.getLogger(RdvConsumer.class);

   public abstract class TibcorvConsumer extends ConsumerWorker implements TibrvMsgCallback {

	public TibcorvConsumer(int index, String name) {
	   super(index, name);
	}

   }

   // RDV subject list
   private final List rdvSubjectList;

   private final RdvTransport transport;

   private TibrvDispatcher tibrvDispatcher;

   /**
    * @param context
    */
   public RdvConsumer(final RuntimeContext context) throws TransportException {

	super(context);

	// coherence check
	checkContext(context);

	// listener config and transport
	this.rdvSubjectList = context.getStringList(RDV_SUBJECTS);
	this.transport = (RdvTransport) super.transport;
   }

   /**
    * Consistency check
    * 
    * @param context
    * @throws TransportException
    */
   protected void checkContext(final RuntimeContext context) throws TransportException {

	if (StringHelper.isEmpty(context.getString(RDV_SUBJECTS))) { throw new EmptyContextParameterException(RDV_SUBJECTS, context); }

	if (!(getTransport() instanceof RdvTransport)) { throw new IllegalContextParameterException(TRANSPORT_REF, context.getString(TRANSPORT_REF), context,
		MessagingMessageBundle.getMessage("messaging.consumer.rdv.transport.illegal", context.getString(TRANSPORT_REF))); }
   }

   @Override
   public synchronized void start() throws TransportException {
	super.start();

	if (tibrvDispatcher == null) {
	   // Start the dispatcher so we don't need to worry about it in this thread
	   this.tibrvDispatcher = new TibrvDispatcher("RDVConsumer" + getName(), Tibrv.defaultQueue());
	}
   }

   @Override
   public synchronized void stop() throws TransportException {
	super.stop();
	if (this.tibrvDispatcher != null) {
	   this.tibrvDispatcher.destroy();
	   this.tibrvDispatcher = null;
	}
   }

   /*
    * (non-Javadoc)
    * @see org.kaleidofoundry.messaging.AbstractConsumer#newWorker(java.lang.String, int)
    */
   @Override
   protected ConsumerWorker newWorker(String workerName, int workerIndex) throws TransportException {

	return new TibcorvConsumer(workerIndex, workerName) {

	   // handle the communication between the tibcoRDV listener (method TibrvMsgCallback#onMsg) and the consumer receive method
	   private final ThreadLocal threadLocalForMessage = new ThreadLocal();

	   // listeners on reliable transport for subject
	   private Map reliableListenerBySubject;
	   // listeners on certified transport for subject
	   private Map certifiedListenerBySubject;

	   @Override
	   public void init() throws TransportException {

		// memorize reliable listener list
		this.reliableListenerBySubject = new ConcurrentHashMap();
		this.certifiedListenerBySubject = new ConcurrentHashMap();

		// Listeners instantiation for each suject
		for (final String rdvSuject : rdvSubjectList) {
		   try {

			// listener on Reliable Transport
			if (RdvTransportTypeEnum.RELIABLE.equals(transport.getType())) {
			   final TibrvListener rdvListener = new TibrvListener(Tibrv.defaultQueue(), this, transport.getRdvTransport(), rdvSuject, null);
			   // memorize listener instance
			   reliableListenerBySubject.put(rdvSuject, rdvListener);
			}

			// Listener on Certified Transport
			if (RdvTransportTypeEnum.CERTIFIED.equals(transport.getType())) {
			   // event queue
			   final TibrvQueue queue = new TibrvQueue();

			   // Create listener for CM messages
			   final TibrvCmListener cmListener = new TibrvCmListener(queue, this, transport.getRdvCmTransport(), rdvSuject, null);

			   // memorize listener instance
			   certifiedListenerBySubject.put(rdvSuject, cmListener);

			   // Set explicit confirmation
			   cmListener.setExplicitConfirm();
			}

			// TODO Listener on Dqueue Transport

		   } catch (final TibrvException rdve) {
			throw new TransportException("messaging.consumer.rdv.create", rdve, rdvSuject);
		   }
		}

		// Queue dispatch TibRv event for reliable transport
		if (RdvTransportTypeEnum.RELIABLE.equals(transport.getType())) {
		   boolean stopped = false;
		   while (!stopped) {
			try {
			   Tibrv.defaultQueue().dispatch();
			} catch (final TibrvException rdve) {
			   throw new TransportException("messaging.consumer.rdv.queue.dispatch", rdve, rdve.getMessage());
			} catch (final InterruptedException ie) {
			   stopped = true;
			}
		   }
		}

		// Queue dispatch TibRv event for certified transport
		if (RdvTransportTypeEnum.CERTIFIED.equals(transport.getType())) {
		   for (final TibrvCmListener listener : certifiedListenerBySubject.values()) {
			final TibrvQueue queue = listener.getQueue();
			new TibrvDispatcher(queue);
		   }
		}
	   }

	   @Override
	   public void receive(MessageWrapper messageWrapper) {

		long messageTimeout = context.getLong(CONSUMER_RECEIVE_TIMEOUT_PROPERTY, -1l);

		if (messageTimeout < 0) {
		   LOGGER.debug("{} waiting incoming messages...", getName());
		} else {
		   LOGGER.debug("{} waiting incoming messages for {}ms...", getName(), messageTimeout);
		}

		try {
		   // wait onMsg call
		   if (messageTimeout < 0) {
			wait();
		   } else {
			wait(messageTimeout);
		   }

		   TibrvMsg tibrvMsg = threadLocalForMessage.get();

		   if (tibrvMsg != null) {
			try {
			   messageWrapper.setProviderObject(tibrvMsg);
			   messageWrapper.setMessage(toMessage(tibrvMsg));
			} finally {
			   threadLocalForMessage.remove();
			}
		   }

		} catch (InterruptedException ite) {
		   messageWrapper.setError(ite);
		} catch (MessagingException mse) {
		   messageWrapper.setError(mse);
		}
	   }

	   @Override
	   public void onMsg(final TibrvListener rdvListener, final TibrvMsg rdvMessage) {
		threadLocalForMessage.set(rdvMessage);
		// notify receive method
		notify();
	   }

	   @Override
	   public void acknowledge(MessageWrapper messageWrapper) throws MessagingException {

		TibrvMsg rdvMessage = (TibrvMsg) messageWrapper.getProviderObject();

		// Only for Certified Transport
		if (RdvTransportTypeEnum.CERTIFIED.equals(transport.getType())) {

		   try {
			// Report we are confirming message
			final long seqno = TibrvCmMsg.getSequence(rdvMessage);

			// If it was not CM message or very first message
			// we'll get seqno=0. Only confirm if seqno > 0.
			if (seqno > 0) {
			   LOGGER.debug("Confirming message with seqno={}", seqno);

			   // get TibrvCmListener for subject
			   final TibrvCmListener cmListener = certifiedListenerBySubject.get(rdvMessage.getSendSubject());

			   // confirm the message
			   cmListener.confirmMsg(rdvMessage);
			}
		   } catch (final TibrvException tibe) {
			throw new MessageException("messaging.consumer.rdv.message.confirm", tibe, tibe.getMessage());
		   }

		   // if message had the reply subject, send the reply
		   try {
			if (rdvMessage.getReplySubject() != null) {
			   final TibrvMsg reply = new TibrvMsg(rdvMessage.getAsBytes());
			   transport.getRdvCmTransport().sendReply(reply, rdvMessage);
			}
		   } catch (final TibrvException tibe) {
			throw new TransportException("messaging.consumer.rdv.message.reply", tibe, tibe.getMessage());
		   }
		}
	   }

	   @Override
	   public void destroy() {
		super.destroy();

		if (reliableListenerBySubject != null) {
		   for (final TibrvListener listener : reliableListenerBySubject.values()) {
			listener.destroy();
		   }
		}

		if (certifiedListenerBySubject != null) {
		   for (final TibrvCmListener listener : certifiedListenerBySubject.values()) {
			listener.destroy();
		   }
		}
	   }

	};
   }

   /**
    * Convert RDV message, to generic message
    * 
    * @param rdvMessage
    * @return
    */
   public Message toMessage(final TibrvMsg rdvMessage) throws MessagingException {
	Message message = null;
	final Map msgParam = new HashMap();

	// For each field of RDV Message, copy field name and value
	for (int i = 0; i < rdvMessage.getNumFields(); i++) {

	   try {
		final TibrvMsgField f = rdvMessage.getFieldByIndex(i);
		msgParam.put(f.name, f.data);

		// Exclude reserved field from parameters ??

	   } catch (final TibrvException tibe) {
		throw new MessageException("messaging.consumer.rdv.message.build", tibe, tibe.getMessage());
	   }
	}

	// Determine main body of the message
	try {

	   final Object msgType = rdvMessage.getField(MESSAGE_TYPE_FIELD).data;
	   final Object messageUuid = rdvMessage.getField(MESSAGE_ID_FIELD).data;

	   // providerId
	   if (messageUuid != null && message instanceof AbstractMessage) {
		((AbstractMessage) message).setProviderId(String.valueOf(messageUuid));
	   }

	   // Text Message
	   if (MessageTypeEnum.Text.getCode().equals(msgType)) {
		final Object messageBody = rdvMessage.get(MESSAGE_BODY_TEXT_FIELD);
		message = new TextMessage((String) messageBody, msgParam);
	   }

	   // XML Message
	   if (MessageTypeEnum.Xml.getCode().equals(msgType)) {
		final Object messageBody = rdvMessage.get(MESSAGE_BODY_TEXT_FIELD);
		message = new XmlMessage((String) messageBody, msgParam);
	   }

	   // Binary Message
	   if (MessageTypeEnum.Bytes.getCode().equals(msgType)) {
		final Object messageBody = rdvMessage.get(MESSAGE_BODY_BYTES_FIELD);
		message = new BytesMessage((byte[]) messageBody, msgParam);
	   }

	   // JavaBean Message
	   if (MessageTypeEnum.JavaBean.getCode().equals(msgType)) {
		final Object messageBody = rdvMessage.get(MESSAGE_BODY_BYTES_FIELD);
		if (messageBody != null) {
		   // Deserialize binary data
		   try {
			final byte[] bytes = (byte[]) messageBody;
			final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
			final Object serializedInstance = in.readObject();
			in.close();
			message = new JavaBeanMessage((Serializable) serializedInstance, msgParam);

		   } catch (final ClassNotFoundException cnfe) {
			throw new MessageException("messaging.consumer.rdv.message.deserialize.classnotfound", cnfe, cnfe.getMessage());
		   } catch (final IOException ioe) {
			throw new MessageException("messaging.consumer.rdv.message.deserialize", ioe, ioe.getMessage());
		   }
		}
	   }

	   return message;

	} catch (final TibrvException tibe) {
	   throw new MessageException("messaging.consumer.rdv.message.build", tibe, tibe.getMessage());
	}
   }

   /**
    * @return RDV Subject messages to listen
    */
   public Iterator getRdvSubjects() {
	return rdvSubjectList.iterator();
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy