CleanableMessagingService.java
package fr.sii.ogham.core.service;
import java.io.Closeable;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.sii.ogham.core.builder.registry.CleanableRegistry;
import fr.sii.ogham.core.clean.Cleanable;
import fr.sii.ogham.core.exception.MessagingException;
import fr.sii.ogham.core.exception.clean.CleanException;
import fr.sii.ogham.core.exception.clean.CloseException;
import fr.sii.ogham.core.message.Message;
/**
* Service that decorated the regular service to also provide the possibility to
* clean resources.
*
* <p>
* Usually, the {@link MessagingService} in instantiated once per application
* and is closed when the application exists. But for any reason, a developer
* may want to create a new service several times. For example, he may create a
* new service everytime a message has to be sent (even if it is less efficient,
* we are not here to make judgments). In this case, the previously created
* service may need to be destroyed and all associated resources should be
* cleaned up.
*
* This service provides three ways to do some cleanup: manually, using
* auto-close behavior using try-with-resource or automatically before garbage
* collection (<strong>/!\ Read information below to understand the
* risks</strong>).
*
* <p>
* This service implements {@link Cleanable} interface to provide a way to
* manually clean resources (by explicitly calling {@link #clean()} method).
*
* <p>
* This service also implements {@link Closeable} interface to automatically
* cleanup resources using the try-with-resource capabilities:
*
* <pre>
* {@code
* try (CleanableMessagingService service = (CleanableMessagingService) builder.build()) {
* service.send(new Email());
* }
* }
* </pre>
*
* In Spring environment, the {@link #close()} is automatically called when the
* enclosing ApplicationContext is closed.
*
* <p>
* This service also overrides the {@code finalize()} method so that if the
* service is garbage collected (no more references on the service instance),
* all resources are automatically cleaned. The purpose is to provide an easy to
* use service that tries to do some cleanup even if the developer forgets to do
* so.
*
* <strong>However, be careful as stated by Sonar: The {@code Object.finalize()}
* method is called on an object by the garbage collector when it determines
* that there are no more references to the object. But there is absolutely no
* warranty that this method will be called AS SOON AS the last references to
* the object are removed. It can be few microseconds to few minutes later. So
* when system resources need to be disposed by an object, it's better to not
* rely on this asynchronous mechanism to dispose them.</strong>
*
* <p>
* The cleanup of resources happens only once per {@link Cleanable}. It means
* that if the developer explicitly calls {@link #clean()}, all resources are
* cleaned up and then if service is garbage collected, the resources are not
* cleaned a second time.
*
*
* @author Aurélien Baudet
* @see Cleanable
* @see CleanableRegistry
* @see AutoCloseable
* @see "https://www.javaworld.com/article/2076697/object-finalization-and-cleanup.html"
*
*/
public class CleanableMessagingService implements MessagingService, Cleanable, Closeable {
private static final Logger LOG = LoggerFactory.getLogger(CleanableMessagingService.class);
private final MessagingService delegate;
private final Cleanable cleaner;
/**
* Wraps the service and delegates cleaning operations to a dedicated
* implementation.
*
* @param delegate
* the wrapped service
* @param cleaner
* the implementation that will really do some cleanup
*/
public CleanableMessagingService(MessagingService delegate, Cleanable cleaner) {
super();
this.delegate = delegate;
this.cleaner = cleaner;
}
@Override
public void send(Message message) throws MessagingException {
delegate.send(message);
}
@Override
public void clean() throws CleanException {
LOG.info("Manually cleaning all resources...");
cleaner.clean();
LOG.info("Manually cleaned");
}
@Override
public void close() throws IOException {
try {
LOG.info("Automatically closing all resources...");
cleaner.clean();
LOG.info("Automatically closed");
} catch (CleanException e) {
throw new CloseException("Failed to close resources", e);
}
}
@Override
@SuppressWarnings("squid:ObjectFinalizeOverridenCheck") // This is
// intentional for
// automatic cleanup
// if developer
// forgets it
protected void finalize() throws Throwable {
try {
LOG.info("Automatically cleaning all resources...");
cleaner.clean();
LOG.info("Automatically cleaned");
} finally {
super.finalize();
}
}
}