Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is not possible to redeploy a shared MailClient Verticle #170

Open
yazalulloa opened this issue Oct 27, 2021 · 5 comments
Open

Is not possible to redeploy a shared MailClient Verticle #170

yazalulloa opened this issue Oct 27, 2021 · 5 comments
Labels

Comments

@yazalulloa
Copy link

I'm trying to redeploy a Verticle with Mail Client, trying to change the configuration of the client without restarting the application.

I'm getting this error:

java.lang.IllegalStateException: Client is closed
at io.vertx.core.net.impl.NetClientImpl.checkClosed(NetClientImpl.java:176)
at io.vertx.core.net.impl.NetClientImpl.connectInternal(NetClientImpl.java:220)
at io.vertx.core.net.impl.NetClientImpl.connect(NetClientImpl.java:207)
at io.vertx.core.net.impl.NetClientImpl.connect(NetClientImpl.java:122)
at io.vertx.core.net.impl.NetClientImpl.connect(NetClientImpl.java:117)
at io.vertx.core.net.impl.NetClientImpl.connect(NetClientImpl.java:107)
at io.vertx.core.net.impl.NetClientImpl.connect(NetClientImpl.java:102)
at io.vertx.ext.mail.impl.SMTPEndPoint.connect(SMTPEndPoint.java:70)
at io.vertx.core.net.impl.pool.SimpleConnectionPool.connect(SimpleConnectionPool.java:263)
at io.vertx.core.net.impl.pool.SimpleConnectionPool$Acquire$3.run(SimpleConnectionPool.java:591)
at io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:50)
at io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:254)
at io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:634)
at io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:638)
at io.vertx.ext.mail.impl.SMTPEndPoint.requestConnection(SMTPEndPoint.java:61)
at io.vertx.core.net.impl.pool.Endpoint.getConnection(Endpoint.java:41)
at io.vertx.ext.mail.impl.SMTPConnectionPool.getConnection0(SMTPConnectionPool.java:155)
at io.vertx.ext.mail.impl.SMTPConnectionPool.getConnection(SMTPConnectionPool.java:102)
at io.vertx.ext.mail.impl.MailClientImpl.getConnection(MailClientImpl.java:119)
at io.vertx.ext.mail.impl.MailClientImpl.lambda$sendMail$2(MailClientImpl.java:103)
at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:832)

This is the code:

import io.vertx.config.ConfigRetriever;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.impl.ConcurrentHashSet;
import io.vertx.core.impl.cpu.CpuCoreSensor;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.mail.MailClient;
import io.vertx.ext.mail.MailConfig;
import io.vertx.ext.mail.MailMessage;

public class MailRedeployTest {

    public static void main(String[] args) {

        final var availableProcessors = CpuCoreSensor.availableProcessors();
        final var vertx = Vertx.vertx();

        final var fileStore = new ConfigStoreOptions()
                .setType("file")
                .setFormat("yaml")
                .setConfig(new JsonObject()
                        .put("path", "mail_config.yaml")
                );
        final var configRetrieverOptions = new ConfigRetrieverOptions().addStore(fileStore).setScanPeriod(5000);
        final var retriever = ConfigRetriever.create(vertx, configRetrieverOptions);

        final var set = new ConcurrentHashSet<String>();

        retriever.getConfig()
                .map(json -> new DeploymentOptions().setConfig(json).setInstances(availableProcessors))
                .flatMap(deploymentOptions -> vertx.deployVerticle(MailVerticle::new, deploymentOptions))
                .onSuccess(set::add)
                .onComplete(ar -> {

                    if (ar.failed()) {
                        System.out.println("ERROR");
                        ar.cause().printStackTrace();
                        System.exit(1);
                    } else {
                        System.out.println("STARTED");
                    }

                    vertx.setPeriodic(10000, l -> {
                        final var mailMessage = new MailMessage()
                                .setSubject("TEST")
                                .setText("TEST")
                                .setTo("[email protected]")
                                .toJson();

                        vertx.eventBus().sender(MailVerticle.ADDRESS).write(mailMessage);
                    });
                });

        retriever.listen(configChange -> {
            System.out.println("CONFIG_CHANGE");
            final var deploymentOptions = new DeploymentOptions().setConfig(configChange.getNewConfiguration()).setInstances(availableProcessors);
            vertx.deployVerticle(MailVerticle::new, deploymentOptions);

            set.forEach(str -> {
                vertx.undeploy(str)
                        .onComplete(ar -> {
                            set.remove(str);

                            if (ar.failed()) {
                                System.out.println("FAILED_TO_UNDEPLOY");
                                ar.cause().printStackTrace();
                            }
                        });
            });
        });


    }


    public static final class MailVerticle extends AbstractVerticle {
        public static final String ADDRESS = "send-mail";
        private MailClient mailClient;


        @Override
        public void start() throws Exception {
            System.out.println("STARTING");
            final var mailConfig = new MailConfig(config());
            mailClient = MailClient.createShared(vertx, mailConfig);
            vertx.eventBus().consumer(ADDRESS, this::sendMail);
        }

        @Override
        public void stop(Promise<Void> promise) throws Exception {
            try {
                System.out.println("STOPPING");
                mailClient.close();
            } catch (Exception e) {
                System.out.println("ERROR_CLOSING_CLIENT");
                e.printStackTrace();
            }

            mailClient = null;
            promise.complete();
        }

        private void sendMail(Message<JsonObject> message) {

            final var mailMessage = new MailMessage(message.body());

            mailMessage.setFrom(config().getString("from"));

            mailClient.sendMail(mailMessage, ar -> {
                if (ar.failed()) {

                    System.out.println("ERROR_SENDING_MESSAGE");
                    ar.cause().printStackTrace();

                } else {
                    System.out.println("MESSAGE_SENT");
                }

            });
        }


    }
}

this is mail_config.yaml:

from: "[email protected]>"
mail_pool: MAIL_POOL
hostname: {hostname}
port: 25
starttls: REQUIRED
login: REQUIRED
username: {username}
password: {password}
keepAlive: true
trustAll: false
metricsName: MAIL_CLIENT
ssl: false
tcpKeepAlive: true
useAlpn: true
logActivity: false

I'm using version 4.1.5

What would be the correct way to close a shared MailClient and open a new one?

I tried with creating an individual client (not shared), and works perfectly

@yazalulloa yazalulloa added the bug label Oct 27, 2021
@gaol
Copy link
Member

gaol commented Oct 28, 2021

Should the MailVerticle be un-deployed before deploying them again on config changes?

On the mail client closing, the internal pool with the shared clients gets closed when there is no reference from MailClient instances to it.

The mail client lacks good way to close because the closing actually takes time like sending QUIT for some existing connections in pool, so I made a branch at: https://github.com/gaol/vertx-mail-client/tree/async_mailclient_close to add this support. then mailClient.close(); in your code can be changed to mailClient.close(promise); @yazalulloa would you please give the branch a try to see if it works for you? thanks :)

@yazalulloa
Copy link
Author

I have no idea how to test a branch, If I download the code I get a bunch of dependency errors

@vietj
Copy link
Contributor

vietj commented Nov 4, 2021

you should use the sonatype oss snapshot repository @yazalulloa

@vietj
Copy link
Contributor

vietj commented Nov 4, 2021

        <repository>
          <id>sonatype-nexus-snapshots</id>
          <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
          <layout>default</layout>
          <releases>
            <enabled>false</enabled>
          </releases>
        </repository>

@yazalulloa
Copy link
Author

yazalulloa commented Dec 23, 2021

How do I set the dependency?

I tried this:

        <dependency>
            <groupId>gaol</groupId>
            <artifactId>vertx-mail-client</artifactId>
            <version>async_mailclient_close</version>
        </dependency>

But did not work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

3 participants