11 Replies Latest reply on Nov 2, 2017 9:13 AM by Miroslav Novak

    Wildfly shutdown hook

    Nikolai Velkov Newbie

      Hello, we have some problems with wildfly's shutdown procedure and are looking for a way to hook into the server's shutdown steps. The problem is that we do some jms destination provisioning runtime where we lookup the destinations by their jndi names and start listening for messages on them. We do handle the shutdown with a @PreDestroy however when shutting down wildfly, first the datasources and connection factories are being closed, then our logic starts throwing exceptions because the PreDestroy logic hasn't been called yet but the connection factories have been closed already.

      So my question is, is there a way to hook up to wildfly's shutdown procedure before the datasources and connections have been closed ?

        • 1. Re: Wildfly shutdown hook
          David Lloyd Master

          The only easy way to add a dependency from an EJB to a configured JNDI resource is by using the @Resource annotation.  The container will create a hard dependency whenever it can when it detects such annotations.

          • 2. Re: Wildfly shutdown hook
            Nikolai Velkov Newbie

            We can't do that since we are collecting the destinations (With our own custom annotation) and looking them up runtime via jndi. Oh yeah also forgot to mention, this is wildfly 9.x.

            Also is there a way to tell the container to register a hard dependency when looking up something by jndi ?

            • 3. Re: Wildfly shutdown hook
              Miroslav Novak Master

              Hi Nikolai,

               

              could you elaborate how CFs are used in your application? Are you using pooled-connection-factory?

               

              My point is that all JMS connections should be closed in your EJB logic after every call. Not in @PreDestroy. Also during shutdown no new EJB calls should get executed but I'm not sure if this feature is already in WF9.

               

              Thanks,

              Mirek

              • 4. Re: Wildfly shutdown hook
                Nikolai Velkov Newbie

                Yes we do use pooled connection factory.

                We also have our own solution that spawns threads for message consumers and waits on messages from the jms system and dispatches them to the consumers, thus opening connections and waiting on them, instead of closing the connections after every call. We did this because we needed runtime jms queue/topic provisioning in wildfly's standalone.xml  when we start our application so we implemented our own custom queue annotations.

                • 5. Re: Wildfly shutdown hook
                  Miroslav Novak Master

                  I think that you should use MDB for this use case. MDB would consume message from queue and use JMS message "reply-to" header (getJMSReplyTo() and setJMSReplyTo())to set and get instance of Destination of target queue/topic. Would it suite your use case?

                  • 6. Re: Wildfly shutdown hook
                    Nikolai Velkov Newbie

                    We can't use mdbs because we write the jms destinations in standalone.xml from the same application. So when the server starts > mdbs are loaded before our logic that writes the destinations is loaded and they start throwing exceptions because the destinations don't yet exist in the servers configuration.

                    • 7. Re: Wildfly shutdown hook
                      Miroslav Novak Master

                      Ok, Wildfly provides deployment descriptor which allows to deploy JMS destinations together with MDB/EJB before it's activated. Create new hornetq-jms.xml file in META-INF directory in your deployment with required destinations like:

                      <messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0">
                          <hornetq-server>
                              <jms-destinations>
                                  <jms-queue name="HELLOWORLDMDBQueue">
                                      <entry name="/queue/HELLOWORLDMDBQueue"/>
                                  </jms-queue>
                                  <jms-topic name="HELLOWORLDMDBTopic">
                                      <entry name="/topic/HELLOWORLDMDBTopic"/>
                                  </jms-topic>
                              </jms-destinations>
                          </hornetq-server>
                      </messaging-deployment>
                      

                       

                      Note that if you undeploy this MDB then those destinations will be undeployed as well but this does not mean that messages would be deleted and all messages lost. It will just undeploy destination from JNDI but the actual (core) queue with all messages will still exist. If MDB is redeployed then it will consume messages which were sent to destination before undeployment.

                       

                      Will this help in your use case?

                      • 8. Re: Wildfly shutdown hook
                        Nikolai Velkov Newbie

                        We have considered this but according to Messaging configuration - WildFly 9 - Project Documentation Editor -

                        This feature is primarily intended for development as destinations deployed this way can not be managed with any of the provided management tools (e.g. console, CLI, etc).

                        so no, as we can't use this in a production environment.

                        • 9. Re: Wildfly shutdown hook
                          Miroslav Novak Master

                          It's written there because usually JMS destinations should be known for the given deployment before it's deployed. This does not seem to be your case. The information that they cannot be managed is not fully correct. Destinations created by this way can be managed in CLI as they're located under:

                          /subsystem=messaging/hornetq-server=default/runtime-queue=jms.queue.<queue_name>
                          

                           

                          There is also official way to do that in Java EE 7 deployment descriptor ejb-jar.xml:

                          ...
                          <jms-destination>
                             <name>java:global/jms/MyQueue</name>
                             <interfaceName>javax.jms.Queue</interfaceName>
                             <destinationName>myQueue</destinationName>
                          </jms-destination>
                          ...
                          

                           

                          Another way might be to add following annotation to your MDB:

                          @JMSDestinationDefinition(
                            name="java:app/MyJMSQueue",
                            interfaceName="javax.jms.Queue",
                            destinationName="myQueue1")
                          

                           

                          I hope that something from above will be ok for you. Getting out of options.

                          • 10. Re: Wildfly shutdown hook
                            Nikolai Velkov Newbie

                            That will work if the destinations can be managed via cli however we still need to find a way to provision them before the mdbs. We actually had a little discussion over here and i think there's something wrong with the approach we're using.

                            We are developing an app and want to automate everything as much as possible, so for example let's say we develop a new feature that needs 3 queues. We have 10 servers with our app deployed. We don't want to have to go through all those servers to add the 3 new queues in each standalone. So we introduced a new annotation that our developers use that defines a jms destination. Once the server is started and the ejb that collects those annotations loaded, we just write the destinations in the current server's standalone.xml using the cli java api. As you can see tough, alot of problems occur from our custom solution. One problem is that if we use the normal mdb listeners, they are loaded before our provisioning can happen so they start misbehaving. The other one is the jndi thing, because basically we have dynamic jms destinations, we can't register them via the resource annotations and we can't really create that hard dependency to their destinations, so the listeners that listen on those destinations start misbehaving on server shutdown (because the destinations are closed before the ejbs), and so on....

                            Do you have any general advice on ways to achieve what we are trying to do?

                            • 11. Re: Wildfly shutdown hook
                              Miroslav Novak Master

                              I did a try and this MDB is ok to deploy and undeploy:

                              import javax.annotation.Resource;
                              import javax.ejb.ActivationConfigProperty;
                              import javax.ejb.MessageDriven;
                              import javax.jms.ConnectionFactory;
                              import javax.jms.JMSContext;
                              import javax.jms.JMSDestinationDefinition;
                              import javax.jms.JMSDestinationDefinitions;
                              import javax.jms.JMSException;
                              import javax.jms.JMSProducer;
                              import javax.jms.Message;
                              import javax.jms.MessageListener;
                              import javax.jms.Queue;
                              import javax.jms.TextMessage;
                              
                              @JMSDestinationDefinitions({
                                      @JMSDestinationDefinition(name = "java:jboss/exported/jms/queue/InQueue", // expose it to remote jndi lookup
                                              destinationName = "InQueue",
                                              interfaceName = "javax.jms.Queue"
                                      ),
                                      @JMSDestinationDefinition(name = "java:/jms/queue/OutQueue",
                                              destinationName = "OutQueue",
                                              interfaceName = "javax.jms.Queue")
                              })
                              @MessageDriven(name = "SimpleMdb", activationConfig = {
                                      @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/queue/InQueue"),
                                      @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
                                      @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
                              public class SimpleMdb implements MessageListener {
                              
                                  @Resource(lookup = "java:/JmsXA")
                                  ConnectionFactory cf;
                              
                                  @Resource(lookup = "java:/jms/queue/OutQueue")
                                  Queue outQueue;
                              
                                  public void onMessage(Message rcvMessage) {
                                      System.out.println("Received message: " + rcvMessage);
                                      try (JMSContext jmsContext = cf.createContext()) {
                                          JMSProducer producer = jmsContext.createProducer();
                                          TextMessage replyMessage = jmsContext.createTextMessage("reply for message: " + rcvMessage.getBody(String.class));
                                          producer.send(outQueue, replyMessage);
                                      } catch (JMSException e) {
                                          e.printStackTrace();
                                      }
                                  }
                              }
                              

                               

                              Will this work for you? The other EJBs should be able to lookup it.