聊聊artemis对junit的支持

深渊向深渊呼唤

本文主要研究一下artemis对junit的支持

TestRule

junit-4.12-sources.jar!/org/junit/rules/TestRule.java

public interface TestRule {
    /**
     * Modifies the method-running {@link Statement} to implement this
     * test-running rule.
     *
     * @param base The {@link Statement} to be modified
     * @param description A {@link Description} of the test implemented in {@code base}
     * @return a new statement, which may be the same as {@code base},
     *         a wrapper around {@code base}, or a completely new Statement.
     */
    Statement apply(Statement base, Description description);
}
TestRule定义了apply方法用于修改并返回Statement

ExternalResource

junit-4.12-sources.jar!/org/junit/rules/ExternalResource.java

public abstract class ExternalResource implements TestRule {
    public Statement apply(Statement base, Description description) {
        return statement(base);
    }

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } finally {
                    after();
                }
            }
        };
    }

    /**
     * Override to set up your specific external resource.
     *
     * @throws Throwable if setup fails (which will disable {@code after}
     */
    protected void before() throws Throwable {
        // do nothing
    }

    /**
     * Override to tear down your specific external resource.
     */
    protected void after() {
        // do nothing
    }
}
ExternalResource声明实现了TestRule接口,其apply方法创建一个匿名Statement并重写其evaluate方法,该方法先执行before,在执行base.evaluate(),最后执行after

EmbeddedActiveMQResource

activemq-artemis-2.11.0/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/EmbeddedActiveMQResource.java

public class EmbeddedActiveMQResource extends ExternalResource {

	//......

   /**
    * Invoked by JUnit to setup the resource - start the embedded ActiveMQ Artemis server
    */
   @Override
   protected void before() throws Throwable {
      log.info("Starting {}: {}", this.getClass().getSimpleName(), getServerName());

      this.start();

      super.before();
   }

   /**
    * Invoked by JUnit to tear down the resource - stops the embedded ActiveMQ Artemis server
    */
   @Override
   protected void after() {
      log.info("Stopping {}: {}", this.getClass().getSimpleName(), getServerName());

      this.stop();

      super.after();
   }

   public void start() {
      try {
         server.start();
      } catch (Exception ex) {
         throw new RuntimeException(String.format("Exception encountered starting %s: %s", server.getClass().getName(), this.getServerName()), ex);
      }

      configuration = server.getActiveMQServer().getConfiguration();
   }

   public void stop() {
      if (internalClient != null) {
         internalClient.stop();
         internalClient = null;
      }

      if (server != null) {
         try {
            server.stop();
         } catch (Exception ex) {
            log.warn(String.format("Exception encountered stopping %s: %s", server.getClass().getSimpleName(), this.getServerName()), ex);
         }
      }
   }

	//......

}
EmbeddedActiveMQResource继承了ExternalResource,并覆盖了before及after方法;before执行的是start方法,after执行的是stop方法;start方法主要是执行EmbeddedActiveMQ.start;stop方法主要是执行InternalClient.stop以及EmbeddedActiveMQ.stop

AbstractActiveMQClientResource

activemq-artemis-2.11.0/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/AbstractActiveMQClientResource.java

public abstract class AbstractActiveMQClientResource extends ExternalResource {

	//......

   @Override
   protected void before() throws Throwable {
      super.before();
      start();
   }

   @Override
   protected void after() {
      stop();
      super.after();
   }

   void start() {
      log.info("Starting {}", this.getClass().getSimpleName());
      try {
         sessionFactory = serverLocator.createSessionFactory();
         session = sessionFactory.createSession(username, password, false, true, true, serverLocator.isPreAcknowledge(), serverLocator.getAckBatchSize());
      } catch (RuntimeException runtimeEx) {
         throw runtimeEx;
      } catch (Exception ex) {
         throw new ActiveMQClientResourceException(String.format("%s initialisation failure", this.getClass().getSimpleName()), ex);
      }

      createClient();

      try {
         session.start();
      } catch (ActiveMQException amqEx) {
         throw new ActiveMQClientResourceException(String.format("%s startup failure", this.getClass().getSimpleName()), amqEx);
      }
   }

   void stop() {
      stopClient();
      if (session != null) {
         try {
            session.close();
         } catch (ActiveMQException amqEx) {
            log.warn("ActiveMQException encountered closing InternalClient ClientSession - ignoring", amqEx);
         } finally {
            session = null;
         }
      }
      if (sessionFactory != null) {
         sessionFactory.close();
         sessionFactory = null;
      }
      if (serverLocator != null) {
         serverLocator.close();
         serverLocator = null;
      }

   }

   protected abstract void createClient();

   protected abstract void stopClient();

	//......

}
AbstractActiveMQClientResource继承了ExternalResource,其before方法执行的是start方法,其after方法执行的是stop方法;start方法会创建session,执行createClient,最后执行session.start;stop方法执行stopClient、session.close、sessionFactory.close、serverLocator.close;它定义了createClient、stopClient抽象方法需要子类去实现

ActiveMQProducerResource

activemq-artemis-2.11.0/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/ActiveMQProducerResource.java

public class ActiveMQProducerResource extends AbstractActiveMQClientResource {

	//......

   @Override
   protected void createClient() {
      try {
         if (!session.addressQuery(address).isExists() && autoCreateQueue) {
            log.warn("{}: queue does not exist - creating queue: address = {}, name = {}", this.getClass().getSimpleName(), address.toString(), address.toString());
            session.createQueue(address, address);
         }
         producer = session.createProducer(address);
      } catch (ActiveMQException amqEx) {
         throw new ActiveMQClientResourceException(String.format("Error creating producer for address %s", address.toString()), amqEx);
      }
   }

   @Override
   protected void stopClient() {
      if (producer != null) {
         try {
            producer.close();
         } catch (ActiveMQException amqEx) {
            log.warn("ActiveMQException encountered closing InternalClient ClientProducer - ignoring", amqEx);
         } finally {
            producer = null;
         }
      }
   }

	//......
}
ActiveMQProducerResource继承了AbstractActiveMQClientResource;其createClient方法执行session.createProducer(address);其stopClient方法执行producer.close()

ActiveMQConsumerResource

activemq-artemis-2.11.0/artemis-junit/src/main/java/org/apache/activemq/artemis/junit/ActiveMQConsumerResource.java

public class ActiveMQConsumerResource extends AbstractActiveMQClientResource {
	
	//......

   @Override
   protected void createClient() {
      boolean browseOnly = false;
      try {
         if (!session.queueQuery(queueName).isExists() && autoCreateQueue) {
            log.warn("{}: queue does not exist - creating queue: address = {}, name = {}", this.getClass().getSimpleName(), queueName.toString(), queueName.toString());
            session.createAddress(queueName, RoutingType.MULTICAST, true);
            session.createQueue(queueName, queueName);
         }
         consumer = session.createConsumer(queueName, browseOnly);
      } catch (ActiveMQException amqEx) {
         throw new ActiveMQClientResourceException(String.format("Error creating consumer for queueName %s", queueName.toString()), amqEx);
      }
   }

   @Override
   protected void stopClient() {
      if (consumer != null) {
         try {
            consumer.close();
         } catch (ActiveMQException amqEx) {
            log.warn("Exception encountered closing consumer - ignoring", amqEx);
         } finally {
            consumer = null;
         }
      }
   }

	//......

}
ActiveMQConsumerResource继承了AbstractActiveMQClientResource,其createClient方法执行的是session.createConsumer(queueName, browseOnly);其stopClient方法执行的是consumer.close()

小结

artemis对junit的ExternalResource提供了扩展,对于server端提供了EmbeddedActiveMQResource,对于client端提供了AbstractActiveMQClientResource(ActiveMQProducerResource、ActiveMQConsumerResource)

doc

artemis-junit
栏目