Skip to content

Commit

Permalink
🎨 avoid circular reference #270
Browse files Browse the repository at this point in the history
  • Loading branch information
trydofor committed Jul 17, 2024
1 parent 93a1045 commit e6d019c
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.task.TaskSchedulingProperties;
Expand Down Expand Up @@ -100,14 +101,17 @@ public class TinyMailServiceImpl implements TinyMailService, InitializingBean {
protected TinyMailServiceProp tinyMailServiceProp;
@Setter(onMethod_ = { @Autowired })
protected ResourceLoader resourceLoader;
@Setter(onMethod_ = { @Autowired(required = false) })
protected List<StatusHook> statusHooks;
@Setter(onMethod_ = { @Autowired(required = false) })
protected Map<String, TinyMailLazy> namedLazyBean;
@Setter(onMethod_ = { @Autowired })
protected ObjectProvider<StatusHook> statusHookProvider;
@Setter(onMethod_ = { @Autowired })
protected ObjectProvider<TinyMailLazy> lazyBeanProvider;

// init by afterPropertiesSet
protected ThreadPoolTaskScheduler taskScheduler;

// init by Provider when first call
protected Map<String, TinyMailLazy> lazyBeanHolder;
protected List<StatusHook> statusHookHolder;

protected final PriorityQueue<AsyncMail> asyncMailQueue = new PriorityQueue<>();
protected final TreeMap<Long, ScheduledFuture<?>> asyncMailSched = new TreeMap<>();
Expand Down Expand Up @@ -182,36 +186,24 @@ public long emit(long id, boolean retry, boolean check) {
}
}

protected long doSend(boolean sync, @NotNull TinyMail message, boolean retry) {
final String conf = message.getConf();
final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf);
AssertArgs.notNull(config, "skip tiny-mail conf={} not found", conf);

final WinMailSender po = saveMailSender(config, message);

final TinyMailMessage mailMessage = makeMailMessage(config, po, message);
if (sync) {
return doSyncSend(po, mailMessage, retry, true);
}
else {
return doAsyncSend(po, mailMessage, retry, true);
}
}

protected long doSend(boolean sync, long id, boolean retry, boolean check) {
final WinMailSender po = winMailSenderDao.fetchOneById(id);
AssertArgs.notNull(po, "skip tiny-mail not found by id={}", id);
@Override
public void afterPropertiesSet() {
ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder();
TaskSchedulingProperties scheduler = tinyMailServiceProp.getScheduler();
builder = builder.poolSize(scheduler.getPool().getSize());
builder = builder.threadNamePrefix(scheduler.getThreadNamePrefix());
TaskSchedulingProperties.Shutdown shutdown = scheduler.getShutdown();
builder = builder.awaitTermination(shutdown.isAwaitTermination());
builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());

final String conf = po.getMailConf();
final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf);
AssertArgs.notNull(config, "skip tiny-mail conf={} not found, id={}", conf, id);
taskScheduler = TaskSchedulerHelper.Ttl(builder);
taskScheduler.initialize();
log.info("tiny-mail taskScheduler, prefix=" + taskScheduler.getThreadNamePrefix());

final TinyMailMessage mailMessage = makeMailMessage(config, po, null);
if (sync) {
return doSyncSend(po, mailMessage, retry, check);
}
else {
return doAsyncSend(po, mailMessage, retry, check);
final long idle = tinyMailServiceProp.getBootScan().toMillis();
if (idle > 0) {
log.info("tiny-mail schedule boot-scan after={} ms", idle);
taskScheduler.schedule(this::scanIdle, Instant.ofEpochMilli(ThreadNow.millis() + idle));
}
}

Expand Down Expand Up @@ -374,39 +366,6 @@ protected int scanSync() {
return size;
}

@Override
public void afterPropertiesSet() {
ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder();
TaskSchedulingProperties scheduler = tinyMailServiceProp.getScheduler();
builder = builder.poolSize(scheduler.getPool().getSize());
builder = builder.threadNamePrefix(scheduler.getThreadNamePrefix());
TaskSchedulingProperties.Shutdown shutdown = scheduler.getShutdown();
builder = builder.awaitTermination(shutdown.isAwaitTermination());
builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());

taskScheduler = TaskSchedulerHelper.Ttl(builder);
taskScheduler.initialize();
log.info("tiny-mail taskScheduler, prefix=" + taskScheduler.getThreadNamePrefix());

if (namedLazyBean != null && !namedLazyBean.isEmpty()) {
lazyBeanHolder = new ConcurrentHashMap<>();
for (var en : namedLazyBean.entrySet()) {
TinyMailLazy bean = en.getValue();
TinyMailLazy old = lazyBeanHolder.put(bean.lazyBean(), bean);
if (old != null) {
throw new IllegalStateException("lazy bean name existed, name=" + old.lazyBean() + ", new-bean=" + en.getKey());
}
}
log.info("tiny-mail TinyMailLazy beans, size=" + lazyBeanHolder.size());
}

final long idle = tinyMailServiceProp.getBootScan().toMillis();
if (idle > 0) {
log.info("tiny-mail schedule boot-scan after={} ms", idle);
taskScheduler.schedule(this::scanIdle, Instant.ofEpochMilli(ThreadNow.millis() + idle));
}
}

protected TinyMailMessage makeMailMessage(@NotNull TinyMailConfig config, @NotNull WinMailSender po, @Nullable TinyMail msg) {
final TinyMailMessage message = new TinyMailMessage();
TinyMailConfig.ConfSetter.toAny(message, config);
Expand Down Expand Up @@ -490,6 +449,36 @@ protected WinMailSender saveMailSender(@NotNull TinyMailConfig config, @NotNull
return po;
}

@Nullable
protected TinyMailLazy findLazyBean(String name) {
if (lazyBeanHolder == null) {
Map<String, TinyMailLazy> tmp = new ConcurrentHashMap<>();
for (TinyMailLazy bean : lazyBeanProvider) {
TinyMailLazy old = tmp.put(bean.lazyBean(), bean);
if (old != null) {
log.error("lazy bean name existed, name={}, new-bean={}", old.lazyBean(), bean.getClass());
}
}
log.info("tiny-mail TinyMailLazy beans, size=" + tmp.size());

lazyBeanHolder = tmp.isEmpty() ? Collections.emptyMap() : tmp;
}

return lazyBeanHolder.get(name);
}

@NotNull
protected List<StatusHook> findStatusHook() {
if (statusHookHolder == null) {
List<StatusHook> lst = new ArrayList<>();
for (StatusHook bean : statusHookProvider) {
lst.add(bean);
}
statusHookHolder = lst.isEmpty() ? Collections.emptyList() : lst;
}
return statusHookHolder;
}

/**
* should skip sending, condition not match
*/
Expand Down Expand Up @@ -533,7 +522,7 @@ protected boolean notMatchProp(@NotNull WinMailSender po) {
}

if (po.getMailText() == null) {
var bean = lazyBeanHolder.get(po.getLazyBean());
var bean = findLazyBean(po.getLazyBean());
if (bean == null) {
log.error("stop lazy tiny-mail, not found bean={}, id={}", bean, po.getId());
return true;
Expand Down Expand Up @@ -564,6 +553,39 @@ protected boolean notNextLock(@NotNull WinMailSender po, long now) {
return false;
}

protected long doSend(boolean sync, @NotNull TinyMail message, boolean retry) {
final String conf = message.getConf();
final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf);
AssertArgs.notNull(config, "skip tiny-mail conf={} not found", conf);

final WinMailSender po = saveMailSender(config, message);

final TinyMailMessage mailMessage = makeMailMessage(config, po, message);
if (sync) {
return doSyncSend(po, mailMessage, retry, true);
}
else {
return doAsyncSend(po, mailMessage, retry, true);
}
}

protected long doSend(boolean sync, long id, boolean retry, boolean check) {
final WinMailSender po = winMailSenderDao.fetchOneById(id);
AssertArgs.notNull(po, "skip tiny-mail not found by id={}", id);

final String conf = po.getMailConf();
final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf);
AssertArgs.notNull(config, "skip tiny-mail conf={} not found, id={}", conf, id);

final TinyMailMessage mailMessage = makeMailMessage(config, po, null);
if (sync) {
return doSyncSend(po, mailMessage, retry, check);
}
else {
return doAsyncSend(po, mailMessage, retry, check);
}
}

/**
* next if done lt max or exception and retry
*/
Expand Down Expand Up @@ -629,17 +651,16 @@ else if (exception instanceof MailParseException || exception instanceof Messagi
}

boolean hookStop = false;
if (statusHooks != null) {
for (StatusHook sh : statusHooks) {
try {
if (sh.stop(po, cost, exception)) {
hookStop = true;
}
}
catch (Exception e) {
log.error("should NOT throw in hook, hook-class=" + sh.getClass().getName(), e);

for (StatusHook sh : findStatusHook()) {
try {
if (sh.stop(po, cost, exception)) {
hookStop = true;
}
}
catch (Exception e) {
log.error("should NOT throw in hook, hook-class=" + sh.getClass().getName(), e);
}
}

if (hookStop) {
Expand Down Expand Up @@ -678,7 +699,7 @@ protected void editLazyMail(@NotNull WinMailSender po, @NotNull @Param.Out TinyM

final Long id = po.getId();
final String bn = po.getLazyBean();
var bean = lazyBeanHolder.get(bn);
var bean = findLazyBean(bn);
if (bean == null) {
throw new MailStopException("tiny-mail lazy-edit, not-found bean=" + bn + ", id=" + id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.RestController;
import pro.fessional.wings.silencer.runner.ApplicationReadyEventRunner;
import pro.fessional.wings.silencer.spring.WingsOrdered;
import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled;
import pro.fessional.wings.tiny.mail.controller.MailListController;
import pro.fessional.wings.tiny.mail.database.TinyMailDatabase;
import pro.fessional.wings.tiny.mail.sender.MailConfigProvider;
import pro.fessional.wings.tiny.mail.sender.MailNotice;
import pro.fessional.wings.tiny.mail.sender.MailSenderManager;
import pro.fessional.wings.tiny.mail.sender.MailSenderProvider;
import pro.fessional.wings.tiny.mail.service.TinyMailLazy;
import pro.fessional.wings.tiny.mail.service.TinyMailService;
import pro.fessional.wings.tiny.mail.spring.prop.TinyMailConfigProp;
import pro.fessional.wings.tiny.mail.spring.prop.TinyMailSenderProp;

import java.util.HashMap;
import java.util.Map;

/**
* @author trydofor
* @since 2022-08-03
Expand Down Expand Up @@ -77,4 +84,28 @@ public MailSenderProvider mailSenderProvider(JavaMailSender defaultSender) {
log.info("TinyMail spring-bean mailSenderProvider");
return new MailSenderProvider(defaultSender);
}

/**
* Check if the bean name is duplicated
*/
@Bean
@ConditionalWingsEnabled
public ApplicationReadyEventRunner tinyMailLazyRunner(@NotNull Map<String, TinyMailLazy> lazyMap) {
log.info("TinyMail spring-runs tinyMailLazyRunner");
return new ApplicationReadyEventRunner(WingsOrdered.Lv3Service, ignored -> {
Map<String, TinyMailLazy> map = new HashMap<>();
for (var en : lazyMap.entrySet()) {
TinyMailLazy bean = en.getValue();
TinyMailLazy old = map.put(bean.lazyBean(), bean);
if (old != null) {
throw new IllegalStateException(
"lazy bean name existed, name=" + old.lazyBean() +
", new-bean-name=" + en.getKey() +
", old-bean-class=" + old.getClass()
);
}
}
log.info("tiny-mail TinyMailLazy beans, size=" + map.size());
});
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package pro.fessional.wings.tiny.app.service;

import lombok.Setter;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pro.fessional.wings.slardar.fastjson.FastJsonHelper;
import pro.fessional.wings.tiny.mail.service.TinyMail;
import pro.fessional.wings.tiny.mail.service.TinyMailLazy;
import pro.fessional.wings.tiny.mail.service.TinyMailService;

import java.util.HashSet;

Expand All @@ -16,6 +20,18 @@ public class TestTinyMailLazy implements TinyMailLazy {

private final HashSet<String> exception1st = new HashSet<>();

@Setter(onMethod_ = { @Autowired })
protected TinyMailService tinyMailService;

/**
* Testing::
* Requested bean is currently in creation:
* Is there an unresolvable circular reference?
*/
public void send(TinyMail mail) {
tinyMailService.send(mail, true);
}

@Override
public @Nullable Edit lazyEdit(@Nullable String para) {
if (para == null) return null;
Expand Down

0 comments on commit e6d019c

Please sign in to comment.