diff --git a/pom.xml b/pom.xml index 9d58c1c..f5177d3 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,7 @@ UTF-8 1.7 + 0.0.9 @@ -89,6 +90,11 @@ false + + io.spring.javaformat + spring-javaformat-maven-plugin + ${spring-javaformat-maven-plugin.version} + diff --git a/src/main/java/locksdemo/JdbcLockService.java b/src/main/java/locksdemo/JdbcLockService.java index fd35fc4..6ca7cf8 100644 --- a/src/main/java/locksdemo/JdbcLockService.java +++ b/src/main/java/locksdemo/JdbcLockService.java @@ -32,7 +32,6 @@ /** * @author Dave Syer - * */ public class JdbcLockService implements LockService { @@ -42,10 +41,15 @@ public class JdbcLockService implements LockService { private long expiry = 30000; // 30 seconds private RowMapper rowMapper = new LockRowMapper(); + private String findAllQuery = "SELECT NAME,VALUE,EXPIRES FROM LOCKS"; + private String createQuery = "INSERT INTO LOCKS (NAME,VALUE,EXPIRES) VALUES (?,?,?)"; + private String deleteQuery = "DELETE FROM LOCKS WHERE NAME=? AND VALUE=?"; + private String refreshQuery = "UPDATE LOCKS SET EXPIRES=? WHERE NAME=? AND VALUE=?"; + private String findOneByNameQuery = "SELECT NAME,VALUE,EXPIRES FROM LOCKS WHERE NAME=?"; public JdbcLockService(DataSource dataSource) { @@ -69,8 +73,8 @@ public Lock create(String name) throws LockExistsException { throw new LockExistsException(); } } - lock = new Lock(name, UUID.randomUUID().toString(), new Date( - System.currentTimeMillis() + expiry)); + lock = new Lock(name, UUID.randomUUID().toString(), + new Date(System.currentTimeMillis() + expiry)); jdbcTemplate.update(createQuery, lock.getName(), lock.getValue(), lock.getExpires()); return lock; @@ -80,34 +84,37 @@ public Lock create(String name) throws LockExistsException { @Transactional(isolation = Isolation.REPEATABLE_READ) public boolean release(String name, String value) throws LockNotHeldException { Lock lock = getLock(name); - if (lock!=null) { + if (lock != null) { if (!lock.getValue().equals(value)) { throw new LockNotHeldException(); } if (lock.isExpired()) { throw new LockNotHeldException(); } + int changes = jdbcTemplate.update(deleteQuery, lock.getName(), + lock.getValue()); + return changes > 0; } - int changes = jdbcTemplate.update(deleteQuery, lock.getName(), lock.getValue()); - return changes > 0; + return false; } @Override @Transactional(isolation = Isolation.REPEATABLE_READ) public Lock refresh(String name, String value) throws LockNotHeldException { Lock lock = getLock(name); - if (lock!=null) { + if (lock != null) { if (!lock.getValue().equals(value)) { throw new LockNotHeldException(); } if (lock.isExpired()) { throw new LockNotHeldException(); } - } - int changes = jdbcTemplate.update(refreshQuery, lock.getExpires(), - lock.getName(), lock.getValue()); - if (changes > 0) { - return lock; + + int changes = jdbcTemplate.update(refreshQuery, lock.getExpires(), + lock.getName(), lock.getValue()); + if (changes > 0) { + return lock; + } } throw new LockNotHeldException(); } diff --git a/src/main/java/locksdemo/Lock.java b/src/main/java/locksdemo/Lock.java index d4d6127..6e7cc3b 100644 --- a/src/main/java/locksdemo/Lock.java +++ b/src/main/java/locksdemo/Lock.java @@ -22,6 +22,7 @@ /** * A value object representing a named lock, with a globally unique value and an expiry. + * * @author Dave Syer * */ @@ -33,16 +34,18 @@ public class Lock implements Comparable { * The name of the lock. */ private final String name; + /** * The value of the lock (globally unique, or at least different for locks with the * same name and different expiry). */ private final String value; + /** * The expiry of the lock expressed as a point in time. */ private final Date expires; - + public boolean isExpired() { return expires.before(new Date()); } diff --git a/src/main/java/locksdemo/LockService.java b/src/main/java/locksdemo/LockService.java index ff330ab..aef5d28 100644 --- a/src/main/java/locksdemo/LockService.java +++ b/src/main/java/locksdemo/LockService.java @@ -24,7 +24,7 @@ * holder to prove that he holds the lock. The value is thus unique per lock and per * expiry period (i.e. 2 locks held at different times with the same name will have * different values).. - * + * * @author Dave Syer * */ @@ -32,7 +32,6 @@ public interface LockService { /** * Iterate the existing locks. - * * @return an iterable of all locks */ Iterable findAll(); @@ -42,7 +41,6 @@ public interface LockService { * hold the lock with this name at any given time. Locks expire and can also be * released by the owner, so after either of those events the lock can be acquired by * the same or a different process. - * * @param name the name identifying the lock * @return a Lock containing a value that can be used to release or refresh the lock * @throws LockExistsException @@ -52,7 +50,6 @@ public interface LockService { /** * Release a lock before it expires. Only the holder of a lock can release it, and the * holder must have the correct unique value to prove that he holds it. - * * @param name the name of the lock * @param value the value of the lock (which has to match the value when it was * acquired) @@ -66,7 +63,6 @@ public interface LockService { * hold the lock there will be an exception, but the implementation may not be able to * tell if it was because he formerly held the lock and it expired, or if it simply * was never held. - * * @param name the name of the lock * @param value the value of the lock (which has to match the value when it was * acquired) diff --git a/src/main/java/locksdemo/LocksApplication.java b/src/main/java/locksdemo/LocksApplication.java index fe8a340..6893f5d 100644 --- a/src/main/java/locksdemo/LocksApplication.java +++ b/src/main/java/locksdemo/LocksApplication.java @@ -20,42 +20,49 @@ public class LocksApplication extends WebMvcConfigurerAdapter { public static void main(String[] args) { SpringApplication.run(LocksApplication.class, args); } - + @Bean public LocksController locksController(LockService lockService) { return new LocksController(lockService); } - + @ConditionalOnClass(RedisConnectionFactory.class) @ConditionalOnBean(RedisConnectionFactory.class) @Configuration protected static class RedisLockServiceConfiguration { + @Bean @ConditionalOnMissingBean(LockService.class) public RedisLockService lockService(RedisConnectionFactory connectionFactory) { return new RedisLockService(connectionFactory); } + } @ConditionalOnClass(RedisConnectionFactory.class) @ConditionalOnMissingBean(RedisConnectionFactory.class) @Configuration protected static class FallbackSimpleLockServiceConfiguration { + @Bean @ConditionalOnMissingBean(LockService.class) public SimpleLockService lockService() { return new SimpleLockService(); } + } - @ConditionalOnMissingClass(name="org.springframework.data.redis.connection.RedisConnectionFactory") + @ConditionalOnMissingClass( + name = "org.springframework.data.redis.connection.RedisConnectionFactory") @Configuration protected static class SimpleLockServiceConfiguration { + @Bean @ConditionalOnMissingBean(LockService.class) public SimpleLockService lockService() { return new SimpleLockService(); } + } } diff --git a/src/main/java/locksdemo/LocksController.java b/src/main/java/locksdemo/LocksController.java index 269cdb7..64eaec1 100644 --- a/src/main/java/locksdemo/LocksController.java +++ b/src/main/java/locksdemo/LocksController.java @@ -33,63 +33,63 @@ /** * @author Dave Syer - * */ @RestController @RequestMapping("/") -@RequiredArgsConstructor(onConstructor=@_(@Autowired)) +@RequiredArgsConstructor(onConstructor = @_(@Autowired)) public class LocksController { - + private final LockService service; - @RequestMapping(method=RequestMethod.GET) + @RequestMapping(method = RequestMethod.GET) public Iterable locks() { return service.findAll(); } - @RequestMapping(value="{name}", method=RequestMethod.POST) + @RequestMapping(value = "{name}", method = RequestMethod.POST) public Lock create(@PathVariable String name) { return service.create(name); } - @RequestMapping(value="{name}/{value}", method=RequestMethod.DELETE) - public Map release(@PathVariable String name, @PathVariable String value) { + @RequestMapping(value = "{name}/{value}", method = RequestMethod.DELETE) + public Map release(@PathVariable String name, + @PathVariable String value) { if (!service.release(name, value)) { throw new NoSuchLockException(); } return Collections.singletonMap("status", (Object) "OK"); } - @RequestMapping(value="{name}/{value}", method=RequestMethod.PUT) + @RequestMapping(value = "{name}/{value}", method = RequestMethod.PUT) public Lock refresh(@PathVariable String name, @PathVariable String value) { return service.refresh(name, value); } - + @ExceptionHandler(LockExistsException.class) @ResponseBody public ResponseEntity> lockExists() { Map body = new HashMap(); body.put("status", "INVALID"); body.put("description", "Lock already exists"); - return new ResponseEntity>(body, HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST); } @ExceptionHandler(NoSuchLockException.class) @ResponseBody public ResponseEntity> noSuchLock() { - Map body = new HashMap(); + Map body = new HashMap<>(); body.put("status", "INVALID"); body.put("description", "Lock not found"); - return new ResponseEntity>(body, HttpStatus.NOT_FOUND); + return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); } @ExceptionHandler(LockNotHeldException.class) @ResponseBody public ResponseEntity> lockNotHeld() { - Map body = new HashMap(); + Map body = new HashMap<>(); body.put("status", "INVALID"); body.put("description", "Lock not held (values do not match)"); - return new ResponseEntity>(body, HttpStatus.NOT_FOUND); + return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); } } diff --git a/src/main/java/locksdemo/RedisLockService.java b/src/main/java/locksdemo/RedisLockService.java index df913ea..77230b7 100644 --- a/src/main/java/locksdemo/RedisLockService.java +++ b/src/main/java/locksdemo/RedisLockService.java @@ -15,21 +15,19 @@ */ package locksdemo; +import lombok.Setter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.util.Assert; + import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; -import lombok.Setter; - -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisOperations; -import org.springframework.util.Assert; - /** * @author Dave Syer - * */ public class RedisLockService implements LockService { @@ -42,17 +40,6 @@ public class RedisLockService implements LockService { private final RedisOperations redisOperations; - /** - * The prefix for all lock keys. - * @param prefix the prefix to set for all lock keys - */ - public void setPrefix(String prefix) { - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - this.prefix = prefix; - } - public RedisLockService(RedisConnectionFactory redisConnectionFactory) { Assert.notNull(redisConnectionFactory, "RedisConnectionFactory must not be null"); this.redisOperations = RedisUtils.stringTemplate(redisConnectionFactory); @@ -61,20 +48,18 @@ public RedisLockService(RedisConnectionFactory redisConnectionFactory) { @Override public Iterable findAll() { Set keys = redisOperations.keys(prefix + "*"); - Set locks = new LinkedHashSet(); + Set locks = new LinkedHashSet<>(); for (String key : keys) { - Date expires = new Date(System.currentTimeMillis() + redisOperations.getExpire(key, TimeUnit.MILLISECONDS)); - locks.add(new Lock(nameForKey(key), redisOperations.opsForValue().get(key), expires)); + Date expires = new Date(System.currentTimeMillis() + + redisOperations.getExpire(key, TimeUnit.MILLISECONDS)); + locks.add(new Lock(nameForKey(key), redisOperations.opsForValue().get(key), + expires)); } return locks; } @Override public Lock create(String name) { - String stored = getValue(name); - if (stored != null) { - throw new LockExistsException(); - } String value = UUID.randomUUID().toString(); String key = keyForName(name); if (!redisOperations.opsForValue().setIfAbsent(key, value)) { @@ -88,9 +73,9 @@ public Lock create(String name) { @Override public boolean release(String name, String value) { String stored = getValue(name); - if (stored != null && value.equals(stored)) { + if (value.equals(stored)) { String key = keyForName(name); - redisOperations.delete(key); + redisOperations.delete(key); return true; } if (stored != null) { @@ -103,7 +88,7 @@ public boolean release(String name, String value) { public Lock refresh(String name, String value) { String key = keyForName(name); String stored = getValue(name); - if (stored != null && value.equals(stored)) { + if (value.equals(stored)) { Date expires = new Date(System.currentTimeMillis() + expiry); redisOperations.expire(key, expiry, TimeUnit.MILLISECONDS); return new Lock(name, value, expires); @@ -113,13 +98,13 @@ public Lock refresh(String name, String value) { private String getValue(String name) { String key = keyForName(name); - String stored = redisOperations.opsForValue().get(key); - return stored; + return redisOperations.opsForValue().get(key); } private String nameForKey(String key) { if (!key.startsWith(prefix)) { - throw new IllegalStateException("Key (" + key + ") does not start with prefix (" + prefix + ")"); + throw new IllegalStateException( + "Key (" + key + ") does not start with prefix (" + prefix + ")"); } return key.substring(prefix.length()); } diff --git a/src/main/java/locksdemo/RedisUtils.java b/src/main/java/locksdemo/RedisUtils.java index 9ca6fcb..c25e8b9 100644 --- a/src/main/java/locksdemo/RedisUtils.java +++ b/src/main/java/locksdemo/RedisUtils.java @@ -46,4 +46,5 @@ static RedisOperations stringTemplate( RedisConnectionFactory redisConnectionFactory) { return new StringRedisTemplate(redisConnectionFactory); } + } diff --git a/src/main/java/locksdemo/SimpleLockService.java b/src/main/java/locksdemo/SimpleLockService.java index 77c7bbb..4e6bff1 100644 --- a/src/main/java/locksdemo/SimpleLockService.java +++ b/src/main/java/locksdemo/SimpleLockService.java @@ -28,15 +28,14 @@ /** * @author Dave Syer - * */ @Service @ConfigurationProperties("spring.platform.lock") public class SimpleLockService implements LockService { - private final SimpleInMemoryRepository locks = new SimpleInMemoryRepository(); + private final SimpleInMemoryRepository locks = new SimpleInMemoryRepository<>(); - private final PriorityQueue ordered = new PriorityQueue(); + private final PriorityQueue ordered = new PriorityQueue<>(); @Setter private long expiry = 30000; // 30 seconds diff --git a/src/test/java/locksdemo/AbstractLockServiceTests.java b/src/test/java/locksdemo/AbstractLockServiceTests.java index f07f7a3..4778506 100644 --- a/src/test/java/locksdemo/AbstractLockServiceTests.java +++ b/src/test/java/locksdemo/AbstractLockServiceTests.java @@ -33,14 +33,14 @@ public abstract class AbstractLockServiceTests { private LockService service; protected abstract LockService getLockService(); - + protected abstract void setExpiry(long expires); - + @Before public void init() { service = getLockService(); } - + @Test public void createLock() { assertNotNull(service.create("foo")); diff --git a/src/test/java/locksdemo/ApplicationTests.java b/src/test/java/locksdemo/ApplicationTests.java index a8d8ecf..2fa6107 100644 --- a/src/test/java/locksdemo/ApplicationTests.java +++ b/src/test/java/locksdemo/ApplicationTests.java @@ -21,21 +21,23 @@ @WebAppConfiguration @IntegrationTest("server.port=0") public class ApplicationTests { - + @Value("${local.server.port}") private int port = 0; @Test public void locksLoad() { @SuppressWarnings("rawtypes") - ResponseEntity entity = new TestRestTemplate().getForEntity("http://localhost:" + port, List.class); + ResponseEntity entity = new TestRestTemplate() + .getForEntity("http://localhost:" + port, List.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); } @Test public void createLock() { @SuppressWarnings("rawtypes") - ResponseEntity entity = new TestRestTemplate().postForEntity("http://localhost:" + port + "/foo", "bar", Map.class); + ResponseEntity entity = new TestRestTemplate() + .postForEntity("http://localhost:" + port + "/foo", "bar", Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); } diff --git a/src/test/java/locksdemo/JdbcLockServiceTests.java b/src/test/java/locksdemo/JdbcLockServiceTests.java index 0eaec78..a8782cb 100644 --- a/src/test/java/locksdemo/JdbcLockServiceTests.java +++ b/src/test/java/locksdemo/JdbcLockServiceTests.java @@ -32,47 +32,49 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - /** * @author Dave Syer * */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes=TestConfiguration.class) +@SpringApplicationConfiguration(classes = TestConfiguration.class) @IntegrationTest("spring.datasource.schema=classpath:/locks-schema.sql") public class JdbcLockServiceTests extends AbstractLockServiceTests { @Autowired private JdbcLockService service; - + @Autowired private DataSource dataSource; - + @Before public void init() { super.init(); service.setExpiry(30000); new JdbcTemplate(dataSource).update("DELETE FROM LOCKS"); } - + @Override protected LockService getLockService() { return service; } - + @Override protected void setExpiry(long expiry) { getLockService(); service.setExpiry(expiry); - } + } @Configuration - @Import({DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class}) + @Import({ DataSourceAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class }) protected static class TestConfiguration { + @Bean public JdbcLockService lockService(DataSource dataSource) { return new JdbcLockService(dataSource); } + } - + } diff --git a/src/test/java/locksdemo/RedisLockServiceTests.java b/src/test/java/locksdemo/RedisLockServiceTests.java index e79960e..7965556 100644 --- a/src/test/java/locksdemo/RedisLockServiceTests.java +++ b/src/test/java/locksdemo/RedisLockServiceTests.java @@ -19,7 +19,6 @@ import org.junit.ClassRule; import org.springframework.data.redis.core.RedisOperations; - /** * @author Dave Syer * @@ -27,30 +26,31 @@ public class RedisLockServiceTests extends AbstractLockServiceTests { private RedisLockService service; - + @ClassRule public static RedisServer server = RedisServer.running(); - + @Before public void init() { super.init(); - RedisOperations template = RedisUtils.stringTemplate(server.getResource()); + RedisOperations template = RedisUtils + .stringTemplate(server.getResource()); template.delete("spring.lock.foo"); template.delete("spring.lock.bar"); } - + @Override protected LockService getLockService() { - if (service==null) { + if (service == null) { service = new RedisLockService(server.getResource()); } return service; } - + @Override protected void setExpiry(long expiry) { getLockService(); service.setExpiry(expiry); - } + } } diff --git a/src/test/java/locksdemo/RedisServer.java b/src/test/java/locksdemo/RedisServer.java index e5281a2..1d70d3a 100644 --- a/src/test/java/locksdemo/RedisServer.java +++ b/src/test/java/locksdemo/RedisServer.java @@ -97,9 +97,10 @@ private Statement failOrSkip(Exception exception) { @Override public void evaluate() throws Throwable { - Assume.assumeTrue("Skipping test due to " - + RedisServer.this.resourceDescription - + " not being available", false); + Assume.assumeTrue( + "Skipping test due to " + RedisServer.this.resourceDescription + + " not being available", + false); } }; } @@ -122,7 +123,6 @@ public RedisConnectionFactory getResource() { /** * Perform cleanup of the {@link #resource} field, which is guaranteed to be non null. - * * @throws Exception any exception thrown by this method will be logged and swallowed */ protected void cleanupResource() throws Exception { diff --git a/src/test/java/locksdemo/SimpleLockServiceTests.java b/src/test/java/locksdemo/SimpleLockServiceTests.java index b04c76b..467f5d8 100644 --- a/src/test/java/locksdemo/SimpleLockServiceTests.java +++ b/src/test/java/locksdemo/SimpleLockServiceTests.java @@ -15,7 +15,6 @@ */ package locksdemo; - /** * @author Dave Syer * @@ -23,15 +22,15 @@ public class SimpleLockServiceTests extends AbstractLockServiceTests { private SimpleLockService service = new SimpleLockService(); - + @Override protected LockService getLockService() { return service; } - + @Override protected void setExpiry(long expiry) { service.setExpiry(expiry); - } + } }