Skip to content

add a LockTemplate to simplify working with distributed Locks in Spring Integration #2971

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

Closed
joshlong opened this issue Jun 20, 2019 · 1 comment · Fixed by #8729
Closed
Assignees
Milestone

Comments

@joshlong
Copy link
Member

joshlong commented Jun 20, 2019

Please add a LockTemplate or something to make using the LockRegistry easier. It took a lot of attempts before I figured out the right combination of objects was. I've encapsulated it into a LockTemplate, as shown below, but I'm sure might see use cases I haven't yet addressed here.

import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.util.ReflectionUtils;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

public class LockTemplate {

	private final LockRegistry registry;

	public LockTemplate(LockRegistry registry) {
		this.registry = registry;
	}

	public <T> T executeWithLock(String key, int timeoutDuration, TimeUnit tu, Callable<T> callable) {
		return this.doExecuteWithLock(key, lock -> lock.tryLock(timeoutDuration, tu), callable);
	}

	public <T> T executeWithLock(String key, Callable<T> callable) {
		return this.doExecuteWithLock(key, Lock::tryLock, callable);
	}

	private <T> T doExecuteWithLock(
		String key, ExceptionSwallowingFunction<Lock, Boolean> lockProducer, Callable<T> callable) {

		try {
			Lock lock = registry.obtain(key);
			boolean lockAcquired = lockProducer.apply(lock);
			if (lockAcquired) {
				try {
					return callable.call();
				}
				finally {
					lock.unlock();
				}
			}
		}
		catch (Exception e) {
			ReflectionUtils.rethrowRuntimeException(e);
		}
		return null;
	}

	private interface ExceptionSwallowingFunction<I, O> {
		O apply(I i) throws Exception;
	}
}
@artembilan
Copy link
Member

Hey, @joshlong !

I've seen your Spring Tip on the matter: https://spring.io/blog/2019/06/19/spring-tips-distributed-locks-with-spring-integration.
It is awesome as usual. Thanks for that and sorry that it hasn't been so visible for you in the past.

I think we rally may accept your suggestion. Meanwhile consider to use existing WhileLockedProcessor:

/**
 * A simple strategy callback class that allows you to provide
 * a code that needs to be executed under {@link Lock} provided by
 * {@link LockRegistry}
 * A typical usage would be to provide implementation of {@link #whileLocked()} method and
 * then call {@link #doWhileLocked()}
 *
 * @author Oleg Zhurakousky
 * @since 2.2
 *
 */
public abstract class WhileLockedProcessor {

It use used in the Framework like this, for example:

	WhileLockedProcessor whileLockedProcessor = new WhileLockedProcessor(this.lockRegistry,
				fileToWriteTo.getAbsolutePath()) {

			@Override
			protected void whileLocked() throws IOException {
				if (append && FileWritingMessageHandler.this.newFileCallback != null && !fileToWriteTo.exists()) {
					FileWritingMessageHandler.this.newFileCallback.accept(fileToWriteTo, requestMessage);
				}

				writeStringToFile(fileToWriteTo, append, content);
			}

		};
		whileLockedProcessor.doWhileLocked();

Hope that helps a little.

However it isn't clear why it brings you some pain since the API is fully based on Java Lock and the pattern to use is exactly the same.

Thank you for the report anyway!

@artembilan artembilan added this to the 6.2.0-M3 milestone Sep 6, 2023
@artembilan artembilan self-assigned this Sep 6, 2023
artembilan added a commit to artembilan/spring-integration that referenced this issue Sep 8, 2023
Fixes spring-projects#2971

* Following best practice and well-known patterns with `Jdbc`, `Rest` or `Jms` templates,
introduce `default` methods into `LockRegistry` interface to make it easier to perform
tasks when within a lock.
* Since all the required logic is now covered by those `LockRegistry.executeLocked()` methods,
there is no need in the dedicated abstract `WhileLockedProcessor` class.
Deprecated it for removal in the next version
* Use a new `LockRegistry.executeLocked()` API in the `FileWritingMessageHandler`
instead of just deprecated `WhileLockedProcessor`
* To satisfy Java limitations for checked lambdas, introduce `CheckedCallable` and `CheckedRunnable` utilities
similar to interfaces in the `io.micrometer.observation.Observation`
* Change existing `CheckedFunction` to expose extra generic argument for `Throwable`
* Add dedicated chapter for distributed lock into docs
* Fix some links and typos in the docs
garyrussell added a commit that referenced this issue Sep 11, 2023
* GH-2971: Add `LockRegistry.executeLocked()` API

Fixes #2971

* Following best practice and well-known patterns with `Jdbc`, `Rest` or `Jms` templates,
introduce `default` methods into `LockRegistry` interface to make it easier to perform
tasks when within a lock.
* Since all the required logic is now covered by those `LockRegistry.executeLocked()` methods,
there is no need in the dedicated abstract `WhileLockedProcessor` class.
Deprecated it for removal in the next version
* Use a new `LockRegistry.executeLocked()` API in the `FileWritingMessageHandler`
instead of just deprecated `WhileLockedProcessor`
* To satisfy Java limitations for checked lambdas, introduce `CheckedCallable` and `CheckedRunnable` utilities
similar to interfaces in the `io.micrometer.observation.Observation`
* Change existing `CheckedFunction` to expose extra generic argument for `Throwable`
* Add dedicated chapter for distributed lock into docs
* Fix some links and typos in the docs

* * Fix Javadoc for `CheckedFunction`

* Fix language in docs

Co-authored-by: Gary Russell <[email protected]>

---------

Co-authored-by: Gary Russell <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants