How does Queue or Topic Proxy differ from DestinationImpl?

SHADOW
SHADOW Member Posts: 5

In my previous post I am facing a class cast exception while writing the consumer code.

java.lang.ClassCastException: com.solacesystems.jms.ra.outbound.QueueProxy cannot be cast to weblogic.jms.common.DestinationImplStack Trace for RuntimeException was:java.lang.ClassCastException: com.solacesystems.jms.ra.outbound.QueueProxy cannot be cast to weblogic.jms.common.DestinationImpl

A similar issue had already been occurring when creating a producer to publish messages to my topic. The setup and configurations were all the same except I used a simple J2E web app to be deployed in weblogic. The code was as below:

package main ;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import java.util.Hashtable;

@Path("/publisher")
public class TopicPublisherRest {
	private final String TOPIC_JNDI_NAME = "JNDI/J2C/my/topic";
	private final String CONNECTION_FACTORY_JNDI_NAME = "JNDI/J2C/CF";
    @POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/publishMsg")
 
    public String publishMsg(String msg) throws Exception {
        System.out.printf("TopicPublisherJNDI is connecting to Solace messaging \n");
        Hashtable<String, Object> env = new Hashtable<>();   
        env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
            
        InitialContext initialContext = new InitialContext(env);
        ConnectionFactory connectionFactory = (ConnectionFactory) initialContext.lookup(CONNECTION_FACTORY_JNDI_NAME);
        System.out.printf("Connection Factory "+ connectionFactory + " established \n");
        
        Connection connection = connectionFactory.createConnection();
        System.out.printf("Connection Initialised \n");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        System.out.printf("Session Created \n");
        
        Topic topic = (Topic) initialContext.lookup(TOPIC_JNDI_NAME);
        System.out.printf("Topic " + topic + " targeted \n");          
               
        MessageProducer messageProducer = session.createProducer(topic);
        String dest = topic.getTopicName();
        System.out.printf("Producer Initialised with destination " + dest + " and obj " + topic.getClass() +" \n");
                
        TextMessage message = null ;
    	   try {
     
                message = session.createTextMessage(msg);
                System.out.printf("Message "+ message +" created from received msg " + msg + "\n");
                messageProducer.send(topic, message, DeliveryMode.PERSISTENT, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
                System.out.println("Msg sent successfully \n");
           } 
           catch (JMSException jmsException) {
            	System.out.println("JMSException occured \n");
            	Thread.sleep(3000);
            	return "JMS Exception - Error Code: " + jmsException.getErrorCode() +
                        "\n Message: " + jmsException.getMessage();                       
            }
         
        } catch (Exception e) {
            System.out.println("Exception occured while sending msg \n");
            System.err.println("Error sending message: " + e.getMessage());
            e.printStackTrace();

        }
    	 finally {
        	try{
        	messageProducer.close();
            session.close();
            connection.close();
            initialContext.close();
            } catch (Exception e) {

                System.err.println("Error sending message: " + e.getMessage());
                e.printStackTrace();

            }
        	
        }
        
        return "Message sent successfully: " + message.getText();
   }
}

This was not initially working because of class cast exception similar to what I am facing for the queue consumer:

JMS Exception - Error Code: soljms.error.internal

Message: Error sending message - internal error (com.solacesystems.jms.impl.SolTopicImpl cannot be cast to com.solacesystems.jms.SolDestination)

The configurations were all correctly done and connection, session, messageProducer were all getting established (inferred through debug statements) but still for some reason the message could not be sent.

After a few days of fruitless search I came across a code snippet and I modified a single line in my code to this:

Topic topic = session.createTopic("MyTopic"); //This is the physical name of the topic at Solace.           

and voila, IT WORKED. I dont know why it didnt work with JNDI mapping. Maybe weblogic handles topic class casting differently but the same solution does not work for the consumer code despite many tries.

Can someone explain why this is the case?

Thanks.

Answers

  • Aaron
    Aaron Member, Administrator, Moderator, Employee Posts: 634 admin

    Hi @SHADOW. I just noticed this thread never got a reply on. Have you made any progress on this issue?

    The one thing from your snippets above that strikes me as odd (not necessarily incorrect, I'm just not super familiar): you are using weblogic.jndi.WLInitialContextFactory as your context factory. So, using WebLogic as an external JNDI lookup provider, vs. using Solace com.solacesystems.jndi.SolJNDIInitialContextFactory. Correct? How have you configured the external JNDI? Does it have all the same (Solace-specific) properties as Solace connection factory? Like, since using WebLogic JNDI for initial context, I'm not sure exactly how it would handle a JNDI topic lookup? I mean, it could work, but I'm not sure. Too bad a JMS expert doesn't weigh in here.

    Ah, looking at this sample code here, maybe this is something to help?

    // lookup the connection factory
    SolConnectionFactory cf = (SolConnectionFactory)ctx.lookup(cfName); // lookup the destination
    Object destination = ctx.lookup(destinationName); // Create a JMS Connection instance .
    connection = cf.createConnection(); // Create a non-transacted, client ACK session.
    Session session = connection.createSession(false, SupportedProperty.SOL_CLIENT_ACKNOWLEDGE); // Create the producer and consumer
    MessageConsumer consumer = null;
    MessageProducer producer = null;
    if (destination instanceof Topic) { Topic topic = (Topic)destination; consumer = session.createConsumer(topic); producer = session.createProducer(topic);
    } else if (destination instanceof Queue) { Queue queue = (Queue)destination; consumer = session.createConsumer(queue); producer = session.createProducer(queue);
    } else {

    Maybe not. Although, your casting exception occurs during the send() method, and not here, right?

    Topic topic = (Topic) initialContext.lookup(TOPIC_JNDI_NAME);
    

    So that means that the lookup succeeds? And returns a valid JMS Topic destination. So I'm guessing that maybe the particular send() call you're using is overloaded or not pure JMS, and is expecting a Solace topic vs. JMS topic or something? Hmmm, just checked the JavaDocs, and that looks ok too.

    I'm not sure. Let us know if you have an update!

  • SHADOW
    SHADOW Member Posts: 5

    Hi @Aaron,

     you are using weblogic.jndi.WLInitialContextFactory as your context factory. So, using WebLogic as an external JNDI lookup provider, vs. using Solace com.solacesystems.jndi.SolJNDIInitialContextFactory. Correct? How have you configured the external JNDI? Does it have all the same (Solace-specific) properties as Solace connection factory? 

    Yes that is correct. Since I was trying to integrate my application, which is already deployed on weblogic and uses JMS for status messages, with Solace…I figured it would be convenient to use the same for connection configurations.

    The external JNDI works perfectly for establishing the connection and session objects however the topic or queue does not seem to work with weblogic's implementation for some reason.

    Ah, looking at this sample code here, maybe this is something to help?

    I see that in this code the destination is initialized with "Object" class and later checked whether it is an Instance of Queue or Topic maybe that could be the issue with Weblogic trying to type cast it differently?

    Anyways I did solve the issue by bypassing the JNDI lookup and instead directly giving the actual destination name of my Topic/Queue configured at the Solace.

    Haven't looked at this issue further since our project is still at a POC stage, but will update if I find anything new regarding this.

  • Aaron
    Aaron Member, Administrator, Moderator, Employee Posts: 634 admin

    Anyways I did solve the issue by bypassing the JNDI lookup and instead directly giving the actual destination name of my Topic/Queue configured at the Solace.

    Cool, yeah just using the "physical" name of the destination rather than JNDI. Probably a better way to go anyhow, especially if you want to use Solace features like Topic-to-Queue mapping and dynamic hierarchical topics… can't be publishing to a static topic, won't take advantage.

    The external JNDI works perfectly for establishing the connection and session objects however the topic or queue does not seem to work with weblogic's implementation for some reason.

    In your external JNDI, have you imported some/all the various Solace messaging-specific parameters that we expose in our connection factory? Otherwise, I'm guessing it would just be the Solace defaults for everything.