@Override public Object lock(Class type, Serializable key) { LockableEntityPersister ep = (LockableEntityPersister)getPersister(type); if (ep == null) { throw new CannotAcquireLockException("Cannot lock key [" + key + "]. It is not a persistent instance!"); } final Object lockedObject = ep.lock(key); if (lockedObject != null) { cacheObject(key, lockedObject); lockedObjects.add(lockedObject); } return lockedObject; }
/** * Set cmd status to STOPPED */ @PostMapping("/stop/{cmdId}") public void stopCommand(@PathVariable String cmdId) { try { CmdStatusItem statusItem = new CmdStatusItem(cmdId, CmdStatus.STOPPED, null, true, true); cmdService.updateStatus(statusItem, false); } catch (CannotAcquireLockException e) { // since cmd been locked, cannot change its status throw new IllegalStatusException("Cmd been processed, cannot stop it"); } }
@Test public void should_stop_queued_cmd() throws Throwable { // given: String testUrl = "/node/path-of-node-for-stop/callback"; stubFor(post(urlEqualTo(testUrl)).willReturn(aResponse().withStatus(200))); // when: send cmd without available agent CmdInfo mockCmd = new CmdInfo(ZONE, null, CmdType.RUN_SHELL, "echo hello"); mockCmd.setWebhook("http://localhost:8088" + testUrl); Cmd mockCmdInstance = cmdService.enqueue(mockCmd, 1, 5); Assert.assertNotNull(mockCmdInstance.getId()); Assert.assertNotNull(cmdDao.get(mockCmdInstance.getId())); // wait for send webhook Thread.sleep(2000); // then: verify has webhook callback if no available agent found CountMatchingStrategy countStrategy = new CountMatchingStrategy(CountMatchingStrategy.GREATER_THAN_OR_EQUAL, 1); verify(countStrategy, postRequestedFor(urlEqualTo(testUrl))); // when: set cmd to stop status try { CmdStatusItem statusItem = new CmdStatusItem(mockCmdInstance.getId(), CmdStatus.STOPPED, null, false, true); cmdService.updateStatus(statusItem, false); // wait for send webhook Thread.sleep(1000); // then: countStrategy = new CountMatchingStrategy(CountMatchingStrategy.GREATER_THAN_OR_EQUAL, 2); verify(countStrategy, postRequestedFor(urlEqualTo(testUrl))); } catch (CannotAcquireLockException acquireLockException) { // may raise the exception when this cmd is processing, in api level should return stop cmd failure } }
@Override protected void lockEntry(PersistentEntity persistentEntity, String entityFamily, Serializable id, int timeout) { String redisKey = getRedisKey(entityFamily, id); final TimeUnit milliUnit = TimeUnit.MILLISECONDS; final long waitTime = TimeUnit.SECONDS.toMillis(timeout); final String lockName = lockName(redisKey); int sleepTime = 0; while(true) { if (redisTemplate.setnx(lockName, System.currentTimeMillis()) && redisTemplate.expire(lockName, timeout)) { break; } if (redisTemplate.ttl(lockName) > 0) { try { if (sleepTime > waitTime) { throw new CannotAcquireLockException("Failed to acquire lock on key ["+redisKey+"]. Wait time exceeded timeout."); } // wait for previous lock to expire sleepTime += 500; milliUnit.sleep(500); } catch (InterruptedException e) { throw new CannotAcquireLockException("Failed to acquire lock on key ["+redisKey+"]: " + e.getMessage(), e); } } else { if (redisTemplate.getset(lockName, System.currentTimeMillis()) != null && redisTemplate.expire(lockName, timeout)) { break; } } } }
@Override public void lock(Object o) { LockableEntityPersister ep = (LockableEntityPersister)getPersister(o); if (ep == null) { throw new CannotAcquireLockException("Cannot lock object [" + o + "]. It is not a persistent instance!"); } Serializable id = ep.getObjectIdentifier(o); if (id == null) { throw new CannotAcquireLockException("Cannot lock transient instance [" + o + "]"); } ep.lock(id); }
@Test public void errorCodeTranslation() { SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES); SQLException badSqlEx = new SQLException("", "", 1); BadSqlGrammarException bsgex = (BadSqlGrammarException) sext.translate("task", "SQL", badSqlEx); assertEquals("SQL", bsgex.getSql()); assertEquals(badSqlEx, bsgex.getSQLException()); SQLException invResEx = new SQLException("", "", 4); InvalidResultSetAccessException irsex = (InvalidResultSetAccessException) sext.translate("task", "SQL", invResEx); assertEquals("SQL", irsex.getSql()); assertEquals(invResEx, irsex.getSQLException()); checkTranslation(sext, 5, DataAccessResourceFailureException.class); checkTranslation(sext, 6, DataIntegrityViolationException.class); checkTranslation(sext, 7, CannotAcquireLockException.class); checkTranslation(sext, 8, DeadlockLoserDataAccessException.class); checkTranslation(sext, 9, CannotSerializeTransactionException.class); checkTranslation(sext, 10, DuplicateKeyException.class); SQLException dupKeyEx = new SQLException("", "", 10); DataAccessException dksex = sext.translate("task", "SQL", dupKeyEx); assertTrue("Not instance of DataIntegrityViolationException", DataIntegrityViolationException.class.isAssignableFrom(dksex.getClass())); // Test fallback. We assume that no database will ever return this error code, // but 07xxx will be bad grammar picked up by the fallback SQLState translator SQLException sex = new SQLException("", "07xxx", 666666666); BadSqlGrammarException bsgex2 = (BadSqlGrammarException) sext.translate("task", "SQL2", sex); assertEquals("SQL2", bsgex2.getSql()); assertEquals(sex, bsgex2.getSQLException()); }
public void testErrorCodeTranslation() { SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES); SQLException badSqlEx = new SQLException("", "", 1); BadSqlGrammarException bsgex = (BadSqlGrammarException) sext.translate("task", "SQL", badSqlEx); assertEquals("SQL", bsgex.getSql()); assertEquals(badSqlEx, bsgex.getSQLException()); SQLException invResEx = new SQLException("", "", 4); InvalidResultSetAccessException irsex = (InvalidResultSetAccessException) sext.translate("task", "SQL", invResEx); assertEquals("SQL", irsex.getSql()); assertEquals(invResEx, irsex.getSQLException()); checkTranslation(sext, 5, DataAccessResourceFailureException.class); checkTranslation(sext, 6, DataIntegrityViolationException.class); checkTranslation(sext, 7, CannotAcquireLockException.class); checkTranslation(sext, 8, DeadlockLoserDataAccessException.class); checkTranslation(sext, 9, CannotSerializeTransactionException.class); checkTranslation(sext, 10, DuplicateKeyException.class); SQLException dupKeyEx = new SQLException("", "", 10); DataAccessException dksex = sext.translate("task", "SQL", dupKeyEx); assertTrue("Not instance of DataIntegrityViolationException", DataIntegrityViolationException.class.isAssignableFrom(dksex.getClass())); // Test fallback. We assume that no database will ever return this error code, // but 07xxx will be bad grammar picked up by the fallback SQLState translator SQLException sex = new SQLException("", "07xxx", 666666666); BadSqlGrammarException bsgex2 = (BadSqlGrammarException) sext.translate("task", "SQL2", sex); assertEquals("SQL2", bsgex2.getSql()); assertEquals(sex, bsgex2.getSQLException()); }
@Override public boolean synchronize () throws InterruptedException { int retrieved = 0, updated = 0, deleted = 0; LOGGER.info("Synchronizer#{} started", getId()); try { if (this.copyProduct) { if (checkDownloadTasks()) { retrieved = getAndCopyNewProduct(); } } else { retrieved = getNewProducts(); } if (Thread.interrupted ()) { throw new InterruptedException (); } updated = getUpdatedProducts (); if (Thread.interrupted ()) { throw new InterruptedException (); } deleted = getDeletedProducts (); } catch (LockAcquisitionException | CannotAcquireLockException e) { throw new InterruptedException (e.getMessage ()); } finally { // Writes the database only if there is a modification if (this.dateChanged) { this.syncConf.setConfig("last_created", String.valueOf(this.lastCreated.getTime())); SYNC_SERVICE.saveSynchronizer(this); this.dateChanged = false; } } return retrieved < pageSize && updated < pageSize && deleted < pageSize; }
/** * Convert the given runtime exception to an appropriate exception from the * {@code org.springframework.dao} hierarchy. * Return null if no translation is appropriate: any other exception may * have resulted from user code, and should not be translated. * <p>The most important cases like object not found or optimistic locking failure * are covered here. For more fine-granular conversion, JpaTransactionManager etc * support sophisticated translation of exceptions via a JpaDialect. * @param ex runtime exception that occurred * @return the corresponding DataAccessException instance, * or {@code null} if the exception should not be translated */ public static DataAccessException convertJpaAccessExceptionIfPossible(RuntimeException ex) { // Following the JPA specification, a persistence provider can also // throw these two exceptions, besides PersistenceException. if (ex instanceof IllegalStateException) { return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); } if (ex instanceof IllegalArgumentException) { return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); } // Check for well-known PersistenceException subclasses. if (ex instanceof EntityNotFoundException) { return new JpaObjectRetrievalFailureException((EntityNotFoundException) ex); } if (ex instanceof NoResultException) { return new EmptyResultDataAccessException(ex.getMessage(), 1, ex); } if (ex instanceof NonUniqueResultException) { return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex); } if (ex instanceof QueryTimeoutException) { return new org.springframework.dao.QueryTimeoutException(ex.getMessage(), ex); } if (ex instanceof LockTimeoutException) { return new CannotAcquireLockException(ex.getMessage(), ex); } if (ex instanceof PessimisticLockException) { return new PessimisticLockingFailureException(ex.getMessage(), ex); } if (ex instanceof OptimisticLockException) { return new JpaOptimisticLockingFailureException((OptimisticLockException) ex); } if (ex instanceof EntityExistsException) { return new DataIntegrityViolationException(ex.getMessage(), ex); } if (ex instanceof TransactionRequiredException) { return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); } // If we have another kind of PersistenceException, throw it. if (ex instanceof PersistenceException) { return new JpaSystemException((PersistenceException) ex); } // If we get here, we have an exception that resulted from user code, // rather than the persistence provider, so we return null to indicate // that translation should not occur. return null; }
@Override public Response toResponse(final CannotAcquireLockException exception) { log.error("CannotAcquireLockException exception", exception); return toResponse(Status.CONFLICT, "database-lock", null); }
@DELETE @Path("cannotAcquireLockException") public void throwCannotAcquireLockException() { throw new CannotAcquireLockException("lock"); }
@Test public void toResponse() { final CannotAcquireLockException exception = new CannotAcquireLockException("lock"); check(mock(new CannotAcquireLockExceptionMapper()).toResponse(exception), 409, "{\"code\":\"database-lock\",\"message\":null,\"parameters\":null,\"cause\":null}"); }
@Override public boolean synchronize () throws InterruptedException { int retrieved = 0, updated = 0, deleted = 0; LOGGER.info("Synchronizer#" + getId () + " started"); try { if (this.copyProduct) { retrieved = getAndCopyNewProduct(); } else { retrieved = getNewProducts(); } if (Thread.interrupted ()) { throw new InterruptedException (); } updated = getUpdatedProducts (); if (Thread.interrupted ()) { throw new InterruptedException (); } deleted = getDeletedProducts (); } catch (LockAcquisitionException | CannotAcquireLockException e) { throw new InterruptedException (e.getMessage ()); } finally { // Logs a summary of what it has done this session StringBuilder sb = new StringBuilder ("Synchronizer#"); sb.append (getId ()).append (" done: "); sb.append (retrieved).append (" new Products, "); sb.append (updated).append (" updated Products, "); sb.append (deleted).append (" deleted Products"); sb.append (" from ").append (this.client.getServiceRoot ()); LOGGER.info(sb.toString()); // Writes the database only if there is a modification if (this.dateChanged) { SYNC_SERVICE.saveSynchronizer (this); this.dateChanged = false; } } return retrieved < pageSize && updated < pageSize && deleted < pageSize; }