![JAR search and dependency download from the Maven repository](/logo.png)
org.simplejavamail.internal.batchsupport.BatchSupport Maven / Gradle / Ivy
/*
* Copyright © 2009 Benny Bottema ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.simplejavamail.internal.batchsupport;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.mail.Session;
import lombok.val;
import org.bbottema.clusteredobjectpool.core.api.ResourceKey.ResourceClusterAndPoolKey;
import org.bbottema.genericobjectpool.PoolableObject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simplejavamail.api.internal.batchsupport.LifecycleDelegatingTransport;
import org.simplejavamail.api.mailer.config.OperationalConfig;
import org.simplejavamail.api.mailer.config.TransportStrategy;
import org.simplejavamail.internal.batchsupport.concurrent.NonJvmBlockingThreadPoolExecutor;
import org.simplejavamail.internal.modules.BatchModule;
import org.simplejavamail.internal.util.concurrent.AsyncOperationHelper;
import org.simplejavamail.smtpconnectionpool.SessionTransport;
import org.simplejavamail.smtpconnectionpool.SmtpConnectionPool;
import org.simplejavamail.smtpconnectionpool.SmtpConnectionPoolClustered;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.simplejavamail.internal.batchsupport.BatchException.ERROR_ACQUIRING_KEYED_POOLABLE;
import static org.simplejavamail.internal.batchsupport.ClusterHelper.compareClusterConfig;
import static org.simplejavamail.internal.batchsupport.ClusterHelper.configureSmtpClusterConfig;
/**
* This class only serves to hide the Batch implementation behind an easy-to-load-with-reflection class.
*/
@SuppressWarnings("unused") // it is used through reflection
public class BatchSupport implements BatchModule {
private static final Logger LOGGER = LoggerFactory.getLogger(BatchSupport.class);
// no need to make this static, because this module itself is already static in the ModuleLoader
@Nullable private SmtpConnectionPoolClustered smtpConnectionPool;
/**
* @see BatchModule#executeAsync(String, Runnable)
*/
@Override
public CompletableFuture executeAsync(@NotNull final String processName, @NotNull final Runnable operation) {
return AsyncOperationHelper.executeAsync(processName, operation);
}
/**
* @see BatchModule#executeAsync(ExecutorService, String, Runnable)
*/
@NotNull
@Override
public CompletableFuture executeAsync(@NotNull final ExecutorService executorService, @NotNull final String processName, @NotNull final Runnable operation) {
return AsyncOperationHelper.executeAsync(executorService, processName, operation);
}
/**
* @see BatchModule#createDefaultExecutorService(int, int)
*/
@NotNull
@Override
public ExecutorService createDefaultExecutorService(final int threadPoolSize, final int keepAliveTime) {
return new NonJvmBlockingThreadPoolExecutor(threadPoolSize, keepAliveTime);
}
/**
* @see BatchModule#registerToCluster(OperationalConfig, UUID, Session)
*/
@Override
public void registerToCluster(@NotNull final OperationalConfig operationalConfig, @NotNull final UUID clusterKey, @NotNull final Session session) {
ensureClusterInitialized(operationalConfig);
final ResourceClusterAndPoolKey poolKey = new ResourceClusterAndPoolKey<>(clusterKey, session);
if (!requireNonNull(smtpConnectionPool).isPoolRegistered(poolKey)) {
smtpConnectionPool.registerResourcePool(poolKey);
}
}
private void ensureClusterInitialized(@NotNull OperationalConfig operationalConfig) {
if (smtpConnectionPool == null) {
LOGGER.warn("Starting SMTP connection pool cluster: JVM won't shutdown until the pool is manually closed with mailer.shutdownConnectionPool() (for each mailer in the cluster)");
smtpConnectionPool = new SmtpConnectionPoolClustered(configureSmtpClusterConfig(operationalConfig));
} else if (compareClusterConfig(operationalConfig, smtpConnectionPool.getClusterConfig())) {
LOGGER.warn("Global SMTP Connection pool is already configured with pool defaults from the first Mailer instance, ignoring relevant properties from {}", operationalConfig);
}
}
/**
* @see BatchModule#acquireTransport(UUID, Session, boolean)
*/
@NotNull
@Override
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "This is bullshit, Spotbugs. There's a requireNonNull() right in front of you, you numbnuts")
public LifecycleDelegatingTransport acquireTransport(@NotNull final UUID clusterKey, @NotNull final Session session, boolean stickySession) {
val smtpConnectionPool = requireNonNull(this.smtpConnectionPool, "Connection pool used before it was initialized. This shouldn't be possible.");
checkConfigureOAuth2Token(session);
return ofNullable(getSessionTransportPoolableObject(smtpConnectionPool, clusterKey, session, stickySession))
.map(LifecycleDelegatingTransportImpl::new)
.orElseThrow(() -> new BatchException(format(ERROR_ACQUIRING_KEYED_POOLABLE, session)));
}
@Nullable
private PoolableObject getSessionTransportPoolableObject(SmtpConnectionPoolClustered smtpConnectionPool, UUID clusterKey, Session session, boolean stickySession) {
try {
return stickySession
? smtpConnectionPool.claimResourceFromPool(new ResourceClusterAndPoolKey<>(clusterKey, session))
: smtpConnectionPool.claimResourceFromCluster(clusterKey);
} catch (InterruptedException e) {
throw new BatchException(format(ERROR_ACQUIRING_KEYED_POOLABLE, session), e);
}
}
// since the SMTP connection pool doesn't know about Simple Java Mail,
// it won't know where to look for the OAUTH2 token unless we copy the property
private void checkConfigureOAuth2Token(Session session) {
val props = session.getProperties();
if (props.containsKey(TransportStrategy.OAUTH2_TOKEN_PROPERTY)) {
props.setProperty(SmtpConnectionPool.OAUTH2_TOKEN_PROPERTY,
props.getProperty(TransportStrategy.OAUTH2_TOKEN_PROPERTY));
}
}
/**
* @see BatchModule#shutdownConnectionPools(Session)
*/
@NotNull
@Override
public Future> shutdownConnectionPools(@NotNull Session session) {
if (smtpConnectionPool == null) {
LOGGER.warn("user requested connection pool shutdown, but there is no connection pool to shut down (yet)");
return completedFuture(null);
}
return smtpConnectionPool.shutdownPool(session);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy