@Test public void execute() { List<ReplaceableAttribute> replaceableAttributes = new ArrayList<ReplaceableAttribute>(); replaceableAttributes.add(new ReplaceableAttribute("NAME1", "VALUE1", true)); exchange.getIn().setHeader(SdbConstants.REPLACEABLE_ATTRIBUTES, replaceableAttributes); exchange.getIn().setHeader(SdbConstants.ITEM_NAME, "ITEM1"); UpdateCondition updateCondition = new UpdateCondition("NAME1", "VALUE1", true); exchange.getIn().setHeader(SdbConstants.UPDATE_CONDITION, updateCondition); command.execute(); assertEquals("DOMAIN1", sdbClient.putAttributesRequest.getDomainName()); assertEquals("ITEM1", sdbClient.putAttributesRequest.getItemName()); assertEquals(updateCondition, sdbClient.putAttributesRequest.getExpected()); assertEquals(replaceableAttributes, sdbClient.putAttributesRequest.getAttributes()); }
@Test public void putAttributes() { final List<ReplaceableAttribute> replaceableAttributes = Arrays.asList(new ReplaceableAttribute[] { new ReplaceableAttribute("NAME1", "VALUE1", true)}); final UpdateCondition updateCondition = new UpdateCondition("NAME1", "VALUE1", true); template.send("direct:start", new Processor() { public void process(Exchange exchange) throws Exception { exchange.getIn().setHeader(SdbConstants.OPERATION, SdbOperations.PutAttributes); exchange.getIn().setHeader(SdbConstants.ITEM_NAME, "ITEM1"); exchange.getIn().setHeader(SdbConstants.UPDATE_CONDITION, updateCondition); exchange.getIn().setHeader(SdbConstants.REPLACEABLE_ATTRIBUTES, replaceableAttributes); } }); assertEquals("TestDomain", amazonSDBClient.putAttributesRequest.getDomainName()); assertEquals("ITEM1", amazonSDBClient.putAttributesRequest.getItemName()); assertEquals(updateCondition, amazonSDBClient.putAttributesRequest.getExpected()); assertEquals(replaceableAttributes, amazonSDBClient.putAttributesRequest.getAttributes()); }
/** * Adds a quote and the userID of the user who favorited it to the Favorites table * * @param postID id for specific quote * @param userId facebook id of the user who favorited the quote */ public static void addToFavoriteTable(String postID, String userId) { ReplaceableAttribute favoritedPostID = new ReplaceableAttribute("postID", postID, Boolean.FALSE); ReplaceableAttribute accName = new ReplaceableAttribute("likedBy", userId, Boolean.FALSE); List<ReplaceableAttribute> attrs = new ArrayList<ReplaceableAttribute>(2); attrs.add(favoritedPostID); attrs.add(accName); PutAttributesRequest par = new PutAttributesRequest("Favorites", postID + "_likedBy_" + userId, attrs); try { getInstance().putAttributes(par); } catch (Exception exception) { System.out.println("EXCEPTION = " + exception); } }
/** * Adds a user and the user who will follow them to the Following table * * @param followedId the id of the person whose posts are going to be followed * @param followerId id of user who pressed the follow icon */ public static void addToFollowingTable(String followedId, String followerId) { ReplaceableAttribute followedIdAttr = new ReplaceableAttribute("followedId", followedId, Boolean.FALSE); ReplaceableAttribute followerIdAttr = new ReplaceableAttribute("followerId", followerId, Boolean.FALSE); List<ReplaceableAttribute> attrs = new ArrayList<ReplaceableAttribute>(2); attrs.add(followedIdAttr); attrs.add(followerIdAttr); PutAttributesRequest par = new PutAttributesRequest("Following", followedId + "_followedBy_" + followerId, attrs); try { getInstance().putAttributes(par); } catch (Exception exception) { System.out.println("EXCEPTION = " + exception); } }
private static void assertReplaceableAttributes(PutAttributesRequest request) { assertEquals(6, request.getAttributes().size()); for (ReplaceableAttribute attr : request.getAttributes()) { if (attr.getName().equals(SdbCassandraInstanceDao.ID_KEY)) { assertEquals(String.valueOf(ID), attr.getValue()); assertEquals(false, attr.getReplace()); } else if (attr.getName().equals(SdbCassandraInstanceDao.DATACENTER_KEY)) { assertEquals(DATACENTER, attr.getValue()); assertEquals(true, attr.getReplace()); } else if (attr.getName().equals(SdbCassandraInstanceDao.RACK_KEY)) { assertEquals(RACK, attr.getValue()); assertEquals(true, attr.getReplace()); } else if (attr.getName().equals(SdbCassandraInstanceDao.HOSTNAME_KEY)) { assertEquals(HOSTNAME, attr.getValue()); assertEquals(true, attr.getReplace()); } else if (attr.getName().endsWith(SdbCassandraInstanceDao.PUBLIC_IP_ADDRESS_KEY)) { assertEquals(PUBLIC_IP_ADDRESS, attr.getValue()); assertEquals(true, attr.getReplace()); } else if (attr.getName().endsWith(SdbCassandraInstanceDao.FULLY_QUALIFIED_DOMAIN_NAME_KEY)) { assertEquals(FULLY_QUALIFIED_DOMAIN_NAME, attr.getValue()); assertEquals(true, attr.getReplace()); } else { assertDuplicateAttribute(attr.getName(), attr.getValue()); } } }
/** * {@inheritDoc} */ @Override @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public void putAll(final Map<? extends String, ? extends String> map) { final Collection<ReplaceableAttribute> attrs = new ArrayList<ReplaceableAttribute>(map.size()); for (final Map.Entry<?, ?> entry : map.entrySet()) { attrs.add( new ReplaceableAttribute() .withName(entry.getKey().toString()) .withValue(entry.getValue().toString()) .withReplace(true) ); } this.credentials.aws().putAttributes( new PutAttributesRequest() .withDomainName(this.table) .withItemName(this.label) .withAttributes(attrs) ); }
@Test public void testPutThrowsWhenOptimisticPersisterUninitialised() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("The optimistic persister has not been initialised"); // ACT // Do not initialise the optimistic persister first - so put should throw ReplaceableAttribute testAttribute = new ReplaceableAttribute(); testAttribute.setName("Name"); testAttribute.setValue("Value"); optimisticPersister.put(testItemName, Optional.of(42), testAttribute); }
@Test public void testPutThrowsWhenMaximumNumberOfAttributesIsAlreadyPresent() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("Database put failed"); // Initialiser persister with a max number of attributes equal to the // current number (1 as there is only one active attribute) - so new put's // will be rejected. optimisticPersister.initialise(1, mockLogger); // Configure attributes for database to return - the get is used for logging // only, so does not really matter. GetAttributesRequest simpleDBRequest = new GetAttributesRequest(testSimpleDBDomainName, testItemName); simpleDBRequest.setConsistentRead(true); GetAttributesResult getAttributesResult = new GetAttributesResult(); getAttributesResult.setAttributes(allAttributes); mockery.checking(new Expectations() { { allowing(mockSimpleDBClient).getAttributes(with(equal(simpleDBRequest))); will(returnValue(getAttributesResult)); } }); // ACT ReplaceableAttribute testAttribute = new ReplaceableAttribute(); testAttribute.setName("Name"); testAttribute.setValue("Value"); // This should throw since we already have the max number of attributes. optimisticPersister.put(testItemName, Optional.of(42), testAttribute); }
private void expectCreateBookingToReturnUpdatedBookingsOrThrow(List<Booking> initialBookings, Booking bookingToCreate, Optional<Exception> exceptionToThrow, int numCalls) throws Exception { // The value we use here is arbitrary - what matters is that the call // to put uses the same value that is returned from get - or else we would // get a conditional check failed exception from SimpleDB. Optional<Integer> expectedVersionNumber = Optional.of(4); // Create booking attribute for the booking being created String attributeName = bookingToCreate.getCourt().toString() + "-" + bookingToCreate.getCourtSpan().toString() + "-" + bookingToCreate.getSlot() + "-" + bookingToCreate.getSlotSpan().toString(); ReplaceableAttribute bookingAttribute = new ReplaceableAttribute(); bookingAttribute.setName(attributeName); bookingAttribute.setValue(newName); // Booking creation gets existing bookings before trying to make the new one expectOptimisticPersisterGetToReturnVersionedAttributesOrThrow(expectedVersionNumber, initialBookings, Optional.empty(), numCalls); if (!exceptionToThrow.isPresent()) { mockery.checking(new Expectations() { { exactly(numCalls).of(mockOptimisticPersister).put(with(equal(bookingToCreate.getDate())), with(equal(expectedVersionNumber)), with(equal(bookingAttribute))); } }); } else { mockery.checking(new Expectations() { { exactly(numCalls).of(mockOptimisticPersister).put(with(equal(bookingToCreate.getDate())), with(equal(expectedVersionNumber)), with(equal(bookingAttribute))); will(throwException(exceptionToThrow.get())); } }); } bookingManager.setOptimisticPersister(mockOptimisticPersister); }
private void expectToPutRuleToOptimisticPersister(int expectedVersion, BookingRule ruleToPut) throws Exception { // Set up attributes to be returned from the database's booking rule item ReplaceableAttribute attribute = new ReplaceableAttribute(); attribute.setName(getAttributeNameFromBookingRule(ruleToPut)); String[] datesToExclude = ruleToPut.getDatesToExclude(); attribute.setValue(StringUtils.join(datesToExclude, ",")); mockery.checking(new Expectations() { { oneOf(mockOptimisticPersister).put(with(equal(ruleItemName)), with(Optional.of(expectedVersion)), with(equal(attribute))); } }); }
private void expectToAddOrDeleteRuleExclusionViaOptimisticPersister(int expectedVersion, String dateToExclude, Boolean doAdd, BookingRule ruleToAddExclusionTo) throws Exception { // Set up attribute to be put to the database's booking rule item ReplaceableAttribute replaceableAttribute = new ReplaceableAttribute(); replaceableAttribute.setName(getAttributeNameFromBookingRule(ruleToAddExclusionTo)); List<String> datesToExclude = new ArrayList<>(); datesToExclude.addAll(Arrays.asList(ruleToAddExclusionTo.getDatesToExclude())); if (doAdd) { datesToExclude.add(dateToExclude); } else { datesToExclude.remove(dateToExclude); } if (datesToExclude.size() > 0) { replaceableAttribute.setValue(StringUtils.join(datesToExclude.toArray(), ",")); } else { replaceableAttribute.setValue(""); } replaceableAttribute.setReplace(true); mockery.checking(new Expectations() { { oneOf(mockOptimisticPersister).put(with(equal(ruleItemName)), with(Optional.of(expectedVersion)), with(equal(replaceableAttribute))); } }); }
@Test public void testSetLifecycleStateCorrectlyCallsTheOptimisticPersister_retired_with_url() throws Exception { // Tests happy path for setting RETIRED state - where we also supply a // forwarding Url for the new site. // ARRANGE lifecycleManager.initialise(mockLogger); // Set up a test lifecycle-state item - with an arbitrary version number. ImmutablePair<Optional<Integer>, Set<Attribute>> testItem = new ImmutablePair<Optional<Integer>, Set<Attribute>>( Optional.of(42), new HashSet<Attribute>()); ReplaceableAttribute newStateAttribute = new ReplaceableAttribute(); newStateAttribute.setName("State"); newStateAttribute.setValue("RETIRED"); newStateAttribute.setReplace(true); ReplaceableAttribute newUrlAttribute = new ReplaceableAttribute(); newUrlAttribute.setName("Url"); newUrlAttribute.setValue(exampleForwardingUrl); newUrlAttribute.setReplace(true); mockery.checking(new Expectations() { { exactly(1).of(mockOptimisticPersister).get(with(equal("LifecycleState"))); will(returnValue(testItem)); exactly(1).of(mockOptimisticPersister).put(with(equal("LifecycleState")), with(equal(Optional.of(42))), with(equal(newStateAttribute))); will(returnValue(43)); exactly(1).of(mockOptimisticPersister).put(with(equal("LifecycleState")), with(equal(Optional.of(43))), with(equal(newUrlAttribute))); } }); // ACT lifecycleManager.setLifecycleState(LifecycleState.RETIRED, Optional.of(exampleForwardingUrl)); }
private void doTestSetLifecycleStateCorrectlyCallsTheOptimisticPersister( LifecycleState lifecycleState) throws Exception { // ARRANGE lifecycleManager.initialise(mockLogger); // Set up a test lifecycle-state item - with an arbitrary version number. ImmutablePair<Optional<Integer>, Set<Attribute>> testItem = new ImmutablePair<Optional<Integer>, Set<Attribute>>( Optional.of(42), new HashSet<Attribute>()); ReplaceableAttribute newStateAttribute = new ReplaceableAttribute(); newStateAttribute.setName("State"); newStateAttribute.setValue(lifecycleState.name()); newStateAttribute.setReplace(true); mockery.checking(new Expectations() { { exactly(1).of(mockOptimisticPersister).get(with(equal("LifecycleState"))); will(returnValue(testItem)); exactly(1).of(mockOptimisticPersister).put(with(equal("LifecycleState")), with(equal(Optional.of(42))), with(equal(newStateAttribute))); // Don't want to put the Url attribute in this case - since we're not // retiring. never(mockOptimisticPersister).put(with(equal("LifecycleState")), with(equal(Optional.of(43))), with(anything())); } }); // ACT lifecycleManager.setLifecycleState(lifecycleState, Optional.empty()); }
@Test public void putAttributes() { final List<ReplaceableAttribute> replaceableAttributes = Arrays.asList(new ReplaceableAttribute[] { new ReplaceableAttribute("NAME1", "VALUE1", true)}); final UpdateCondition updateCondition = new UpdateCondition("NAME1", "VALUE1", true); template.send("direct:start", new Processor() { public void process(Exchange exchange) throws Exception { exchange.getIn().setHeader(SdbConstants.OPERATION, SdbOperations.PutAttributes); exchange.getIn().setHeader(SdbConstants.ITEM_NAME, "ITEM1"); exchange.getIn().setHeader(SdbConstants.UPDATE_CONDITION, updateCondition); exchange.getIn().setHeader(SdbConstants.REPLACEABLE_ATTRIBUTES, replaceableAttributes); } }); }
@Test(expected = IllegalArgumentException.class) public void executeWithoutItemName() { List<ReplaceableAttribute> replaceableAttributes = new ArrayList<ReplaceableAttribute>(); replaceableAttributes.add(new ReplaceableAttribute("NAME1", "VALUE1", true)); exchange.getIn().setHeader(SdbConstants.REPLACEABLE_ATTRIBUTES, replaceableAttributes); UpdateCondition updateCondition = new UpdateCondition("NAME1", "VALUE1", true); exchange.getIn().setHeader(SdbConstants.UPDATE_CONDITION, updateCondition); command.execute(); }
@Test public void determineReplaceableAttributes() { assertNull(this.command.determineReplaceableAttributes()); List<ReplaceableAttribute> replaceableAttributes = new ArrayList<ReplaceableAttribute>(); replaceableAttributes.add(new ReplaceableAttribute("NAME1", "VALUE1", true)); exchange.getIn().setHeader(SdbConstants.REPLACEABLE_ATTRIBUTES, replaceableAttributes); assertEquals(replaceableAttributes, this.command.determineReplaceableAttributes()); }
/** * @param item * @return */ private ReplaceableItem itemToAWSItem(Item item) { List<ReplaceableAttribute> attributes = new ArrayList<ReplaceableAttribute>(); for (com.intuit.tank.reporting.databases.Attribute attr : item.getAttributes()) { addAttribute(attributes, attr.getName(), attr.getValue()); } ReplaceableItem ret = new ReplaceableItem(item.getName(), attributes); return ret; }
private List<ReplaceableAttribute> getTimingAttributes(TankResult result) { List<ReplaceableAttribute> attributes = new ArrayList<ReplaceableAttribute>(); String timestamp = ReportUtil.getTimestamp(result.getTimeStamp()); addAttribute(attributes, DatabaseKeys.TIMESTAMP_KEY.getShortKey(), timestamp); addAttribute(attributes, DatabaseKeys.REQUEST_NAME_KEY.getShortKey(), timestamp + "-" + UUID.randomUUID().toString()); addAttribute(attributes, DatabaseKeys.JOB_ID_KEY.getShortKey(), result.getJobId()); addAttribute(attributes, DatabaseKeys.LOGGING_KEY_KEY.getShortKey(), result.getRequestName()); addAttribute(attributes, DatabaseKeys.STATUS_CODE_KEY.getShortKey(), String.valueOf(result.getStatusCode())); addAttribute(attributes, DatabaseKeys.RESPONSE_TIME_KEY.getShortKey(), String.valueOf(result.getResponseTime())); addAttribute(attributes, DatabaseKeys.RESPONSE_SIZE_KEY.getShortKey(), String.valueOf(result.getResponseSize())); addAttribute(attributes, DatabaseKeys.INSTANCE_ID_KEY.getShortKey(), String.valueOf(result.getInstanceId())); addAttribute(attributes, DatabaseKeys.IS_ERROR_KEY.getShortKey(), String.valueOf(result.isError())); return attributes; }
/** * Update attribute values * * @param domainName table name * @param itemName itemName for the item * @param attributes map of attribute name and value to replace */ public static void updateAttributesForItem(String domainName, String itemName, HashMap<String, String> attributes) { List<ReplaceableAttribute> replaceableAttributes = new ArrayList<ReplaceableAttribute>(attributes.size()); for (String attributeName : attributes.keySet()) { replaceableAttributes.add(new ReplaceableAttribute().withName(attributeName).withValue(attributes.get(attributeName)).withReplace(true)); } getInstance().putAttributes(new PutAttributesRequest(domainName, itemName, replaceableAttributes)); }
public static void addPostIDToDomain(String postID, String tableName) { ReplaceableAttribute postIDAttribute = new ReplaceableAttribute("postID", postID, Boolean.FALSE); List<ReplaceableAttribute> attrs = new ArrayList<ReplaceableAttribute>(1); attrs.add(postIDAttribute); PutAttributesRequest par = new PutAttributesRequest(tableName, postID, attrs); try { getInstance().putAttributes(par); } catch (Exception exception) { System.out.println("EXCEPTION = " + exception); } }
private static List<ReplaceableAttribute> buildSaveAttributes(CassandraInstance instance) { List<ReplaceableAttribute> attrs = Lists.newArrayList(); attrs.add(attribute(ID_KEY, String.valueOf(instance.getId()), false)); attrs.add(attribute(DATACENTER_KEY, instance.getDataCenter(), true)); attrs.add(attribute(RACK_KEY, instance.getRack(), true)); attrs.add(attribute(HOSTNAME_KEY, instance.getHostName(), true)); attrs.add(attribute(PUBLIC_IP_ADDRESS_KEY, instance.getPublicIpAddress(), true)); attrs.add(attribute(FULLY_QUALIFIED_DOMAIN_NAME_KEY, instance.getFullyQualifiedDomainName(), true)); return attrs; }
private static PutAttributesRequest createPutAttributesRequest(String domain, String itemName, Map<String, List<String>> chunk) { final PutAttributesRequest putRequest = new PutAttributesRequest(); putRequest.setDomainName(domain); putRequest.setItemName(itemName); List<ReplaceableAttribute> simpleDbAttributes = toReplaceableAttributeList(chunk); putRequest.setAttributes(simpleDbAttributes); return putRequest; }
private static List<ReplaceableAttribute> toReplaceableAttributeList(Map<String, List<String>> attributes) { boolean replace = true; final List<ReplaceableAttribute> result = new ArrayList<ReplaceableAttribute>(); for(final Map.Entry<String, List<String>> entry : attributes.entrySet()) { replace = true; for (final String value : entry.getValue()) { result.add(new ReplaceableAttribute(entry.getKey(), value, replace)); replace = false; } } return result; }
@Override public void setLifecycleState(LifecycleState lifecycleState, Optional<String> newServiceUrl) throws Exception { if (!initialised) { throw new IllegalStateException("The lifecycle manager has not been initialised"); } if (lifecycleState.equals(LifecycleState.RETIRED)) { String message = null; if (!newServiceUrl.isPresent() || !newServiceUrl.get().startsWith("http")) { if (!newServiceUrl.isPresent()) { message = "Throwing exception as url to new service has not been provided when setting lifecycle state to RETIRED"; } else if (!newServiceUrl.get().startsWith("http")) { // Insist on correct format for the forwarding url. message = "Throwing exception as url to new service when setting lifecycle state to RETIRED does not start with http"; } logger.log(message); throw new Exception( "Must provide valid url to new service when setting lifecycle state to RETIRED"); } } // Get attribute - so can set version correctly. We assume only one person // (i.e. the site admin) will ever be calling this at one time, but we // nevertheless supply the version - so we can use the optimistic persister. ImmutablePair<Optional<Integer>, Set<Attribute>> lifecycleStateItem = getOptimisticPersister() .get(lifecycleItemName); logger.log("About to set lifecycle state in database to: " + lifecycleState.name()); ReplaceableAttribute lifecycleAttribute = new ReplaceableAttribute(); lifecycleAttribute.setName("State"); lifecycleAttribute.setValue(lifecycleState.name()); lifecycleAttribute.setReplace(true); int newVersion = getOptimisticPersister().put(lifecycleItemName, lifecycleStateItem.left, lifecycleAttribute); logger.log("Updated lifecycle state in database to: " + lifecycleState.name()); if (newServiceUrl.isPresent()) { logger.log("About to set lifecycle state url in database to: " + newServiceUrl.get()); ReplaceableAttribute urlAttribute = new ReplaceableAttribute(); urlAttribute.setName("Url"); urlAttribute.setValue(newServiceUrl.get()); urlAttribute.setReplace(true); getOptimisticPersister().put(lifecycleItemName, Optional.of(newVersion), urlAttribute); logger.log("Updated lifecycle state url in database to: " + newServiceUrl.get()); } }
@Override public void delete(String itemName, Attribute attribute) throws Exception { if (!initialised) { throw new IllegalStateException("The optimistic persister has not been initialised"); } logger.log("About to delete attribute from simpledb item: " + itemName); AmazonSimpleDB client = getSimpleDBClient(); // We retry the delete if necessary if we get a // ConditionalCheckFailed exception, i.e. if someone else modifies the // database between us reading and writing it. RetryHelper .DoWithRetries(() -> { try { // Get existing attributes (and version number), via consistent // read: ImmutablePair<Optional<Integer>, Set<Attribute>> versionedAttributes = get(itemName); if (!versionedAttributes.left.isPresent()) { logger .log("A version number attribute did not exist - this means no attributes exist, so we have nothing to delete."); return null; } if (!versionedAttributes.right.contains(attribute)) { logger.log("The attribute did not exist - so we have nothing to delete."); return null; } // Since it seems impossible to update the version number while // deleting an attribute, we first mark the attribute as inactive, // and then delete it. ReplaceableAttribute inactiveAttribute = new ReplaceableAttribute(); inactiveAttribute.setName(attribute.getName()); inactiveAttribute.setValue("Inactive" + attribute.getValue()); inactiveAttribute.setReplace(true); put(itemName, versionedAttributes.left, inactiveAttribute); // Now we can safely delete the attribute, as other readers will now // ignore it. UpdateCondition updateCondition = new UpdateCondition(); updateCondition.setName(inactiveAttribute.getName()); updateCondition.setValue(inactiveAttribute.getValue()); // Update will proceed unless the attribute no longer exists updateCondition.setExists(true); Attribute attributeToDelete = new Attribute(); attributeToDelete.setName(inactiveAttribute.getName()); attributeToDelete.setValue(inactiveAttribute.getValue()); List<Attribute> attributesToDelete = new ArrayList<>(); attributesToDelete.add(attributeToDelete); DeleteAttributesRequest simpleDBDeleteRequest = new DeleteAttributesRequest( simpleDbDomainName, itemName, attributesToDelete, updateCondition); client.deleteAttributes(simpleDBDeleteRequest); logger.log("Deleted attribute from simpledb"); return null; } catch (AmazonServiceException ase) { if (ase.getErrorCode().contains("AttributeDoesNotExist")) { // Case of trying to delete an attribute that no longer exists - // that's ok - it probably just means more than one person was // trying to delete the attribute at once. So swallow this // exception logger .log("Caught AmazonServiceException for AttributeDoesNotExist whilst deleting attribute so" + " swallowing and continuing"); return null; } else { throw ase; } } }, Exception.class, Optional.of("Database put failed - conditional check failed"), logger); }
@Override public List<Booking> createBooking(Booking bookingToCreate, boolean isSquashServiceUserCall) throws Exception { if (!initialised) { throw new IllegalStateException("The booking manager has not been initialised"); } getLifecycleManager().throwIfOperationInvalidForCurrentLifecycleState(false, isSquashServiceUserCall); logger.log("About to create booking in database: " + bookingToCreate); // Get today's bookings (and version number), via consistent read: String itemName = bookingToCreate.getDate(); // We retry the creation of the booking if necessary if we get a // ConditionalCheckFailed exception, i.e. if someone else modifies // the database between us reading and writing it. return RetryHelper .DoWithRetries( () -> { ImmutablePair<Optional<Integer>, List<Booking>> versionedBookings = getVersionedBookings(itemName); // Check that the court(s) we're booking is/are currently free // Get individual booked courts as (court, slot) pairs Set<ImmutablePair<Integer, Integer>> bookedCourts = new HashSet<>(); versionedBookings.right.forEach((booking) -> { addBookingToSet(booking, bookedCourts); }); // Get courts we're trying to book as (court, slot) pairs Set<ImmutablePair<Integer, Integer>> courtsToBook = new HashSet<>(); addBookingToSet(bookingToCreate, courtsToBook); // Does the new booking clash with existing bookings? boolean bookingClashes = Boolean.valueOf(Sets .intersection(courtsToBook, bookedCourts).size() > 0); if (bookingClashes) { // Case of trying to book an already-booked slot - this // probably means either: // - more than one person was trying to book the slot at once, // or // - not all courts in our block booking are free logger .log("Cannot book courts which are already booked, so throwing a 'Booking creation failed' exception"); throw new Exception("Booking creation failed"); } logger.log("Required courts are currently free - so proceeding to make booking"); // Do a conditional put - so we don't overwrite someone else's // booking String attributeName = getAttributeNameFromBooking(bookingToCreate); String attributeValue = bookingToCreate.getName(); logger.log("ItemName: " + itemName); logger.log("AttributeName: " + attributeName); logger.log("AttributeValue: " + attributeValue); ReplaceableAttribute bookingAttribute = new ReplaceableAttribute(); bookingAttribute.setName(attributeName); bookingAttribute.setValue(attributeValue); getOptimisticPersister().put(itemName, versionedBookings.left, bookingAttribute); logger.log("Created booking in database"); // Add the booking we've just made to the pre-existing ones. List<Booking> bookings = versionedBookings.right; bookings.add(bookingToCreate); return bookings; }, Exception.class, Optional.of("Database put failed - conditional check failed"), logger); }
@Override public Set<BookingRule> createRule(BookingRule bookingRuleToCreate, boolean isSquashServiceUserCall) throws Exception { if (!initialised) { throw new IllegalStateException("The rule manager has not been initialised"); } lifecycleManager .throwIfOperationInvalidForCurrentLifecycleState(false, isSquashServiceUserCall); // We retry the put of the rule if necessary if we get a // ConditionalCheckFailed exception, i.e. if someone else modifies the // database between us reading and writing it. return RetryHelper .DoWithRetries(() -> { Set<BookingRule> bookingRules = null; // Check that non-recurring rule is not for a date in the past. if (!bookingRuleToCreate.getIsRecurring()) { if ((new SimpleDateFormat("yyyy-MM-dd")).parse( bookingRuleToCreate.getBooking().getDate()).before( new SimpleDateFormat("yyyy-MM-dd").parse(getCurrentLocalDate().format( DateTimeFormatter.ofPattern("yyyy-MM-dd"))))) { logger .log("Cannot add non-recurring booking rule for a date in the past, so throwing a 'Booking rule creation failed' exception"); throw new Exception("Booking rule creation failed"); } } // We should POST or DELETE to the BookingRuleExclusion resource, // with a BookingRule, and an exclusion date. This will call through // to the addBookingRuleExclusion or deleteBookingRuleExclusion // methods on this manager. logger.log("About to create booking rule in simpledb: " + bookingRuleToCreate); ImmutablePair<Optional<Integer>, Set<BookingRule>> versionedBookingRules = getVersionedBookingRules(); bookingRules = versionedBookingRules.right; // Check that the rule we're creating does not clash with an // existing rule. if (doesRuleClash(bookingRuleToCreate, bookingRules)) { logger .log("Cannot create rule as it clashes with existing rule, so throwing a 'Booking rule creation failed - rule would clash' exception"); throw new Exception("Booking rule creation failed - rule would clash"); } logger .log("The new rule does not clash with existing rules - so proceeding to create rule"); String attributeName = getAttributeNameFromBookingRule(bookingRuleToCreate); String attributeValue = ""; if (bookingRuleToCreate.getDatesToExclude().length > 0) { attributeValue = StringUtils.join(bookingRuleToCreate.getDatesToExclude(), ","); } logger.log("ItemName: " + ruleItemName); logger.log("AttributeName: " + attributeName); logger.log("AttributeValue: " + attributeValue); ReplaceableAttribute bookingRuleAttribute = new ReplaceableAttribute(); bookingRuleAttribute.setName(attributeName); bookingRuleAttribute.setValue(attributeValue); optimisticPersister.put(ruleItemName, versionedBookingRules.left, bookingRuleAttribute); bookingRules.add(bookingRuleToCreate); return bookingRules; }, Exception.class, Optional.of("Database put failed - conditional check failed"), logger); }
@Override public Optional<BookingRule> deleteRuleExclusion(String dateNotToExclude, BookingRule bookingRuleToDeleteExclusionFrom, boolean isSquashServiceUserCall) throws Exception { if (!initialised) { throw new IllegalStateException("The rule manager has not been initialised"); } lifecycleManager .throwIfOperationInvalidForCurrentLifecycleState(false, isSquashServiceUserCall); logger.log("About to delete exclusion for " + dateNotToExclude + " from booking rule: " + bookingRuleToDeleteExclusionFrom.toString()); // We retry the deletion of the exclusion if necessary if we get a // ConditionalCheckFailed exception, i.e. if someone else modifies // the database between us reading and writing it. return RetryHelper .DoWithRetries( (ThrowingSupplier<Optional<BookingRule>>) (() -> { ImmutablePair<Optional<Integer>, Set<BookingRule>> versionedBookingRules = getVersionedBookingRules(); Set<BookingRule> existingBookingRules = versionedBookingRules.right; // Check the BookingRule we're deleting the exclusion from still // exists Optional<BookingRule> existingRule = existingBookingRules.stream() .filter(rule -> rule.equals(bookingRuleToDeleteExclusionFrom)).findFirst(); if (!existingRule.isPresent()) { logger .log("Trying to delete an exclusion from a booking rule that does not exist, so swallowing and continuing"); return Optional.empty(); } // Check that the rule exclusion we're deleting still exists. if (!Arrays.asList(existingRule.get().getDatesToExclude()).contains(dateNotToExclude)) { logger .log("The booking rule exclusion being deleted no longer exists - so quitting early"); return Optional.empty(); } // Check deleting this exclusion does not cause a latent rule // clash to manifest. Do by pretending to add this rule again. existingBookingRules.remove(existingRule.get()); Set<String> datesToExclude = Sets.newHashSet(bookingRuleToDeleteExclusionFrom .getDatesToExclude()); datesToExclude.remove(dateNotToExclude); existingRule.get().setDatesToExclude( datesToExclude.toArray(new String[datesToExclude.size()])); if (doesRuleClash(existingRule.get(), existingBookingRules)) { logger .log("Cannot delete booking rule exclusion as remaining rules would then clash"); throw new Exception("Booking rule exclusion deletion failed - latent clash exists"); } logger.log("Proceeding to delete the rule exclusion"); String attributeName = getAttributeNameFromBookingRule(bookingRuleToDeleteExclusionFrom); String attributeValue = StringUtils.join(datesToExclude, ","); logger.log("ItemName: " + ruleItemName); logger.log("AttributeName: " + attributeName); logger.log("AttributeValue: " + attributeValue); ReplaceableAttribute bookingRuleAttribute = new ReplaceableAttribute(); bookingRuleAttribute.setName(attributeName); bookingRuleAttribute.setValue(attributeValue); bookingRuleAttribute.setReplace(true); optimisticPersister.put(ruleItemName, versionedBookingRules.left, bookingRuleAttribute); BookingRule updatedBookingRule = new BookingRule(bookingRuleToDeleteExclusionFrom); updatedBookingRule.setDatesToExclude(datesToExclude.toArray(new String[datesToExclude .size()])); logger.log("Deleted rule exclusion"); return Optional.of(updatedBookingRule); }), Exception.class, Optional.of("Database put failed - conditional check failed"), logger); }
@Test public void testPutDoesNotThrowWhenMaximumNumberOfAttributesIsAlreadyPresentIfPutIsToInactivate() throws Exception { // Our 2-stage deletion process involves an initial put to 'inactivate' the // attribute being deleted. We must allow such put's to exceed the limit - // or else we could never delete attributes when we're over the limit. // ARRANGE // Initialiser persister with a max number of attributes equal to the // current number (1 as there is only one active attribute) - so new put's // will be rejected, unless they're inactivating puts. optimisticPersister.initialise(1, mockLogger); // Configure attributes for database to return - the get is used for logging // only, so does not really matter. GetAttributesRequest simpleDBRequest = new GetAttributesRequest(testSimpleDBDomainName, testItemName); simpleDBRequest.setConsistentRead(true); GetAttributesResult getAttributesResult = new GetAttributesResult(); getAttributesResult.setAttributes(allAttributes); mockery.checking(new Expectations() { { allowing(mockSimpleDBClient).getAttributes(with(equal(simpleDBRequest))); will(returnValue(getAttributesResult)); } }); // Don't care about further calls to SimpleDB. mockery.checking(new Expectations() { { ignoring(mockSimpleDBClient); } }); // ACT // Use an 'inactivating' put i.e. value prefixed with 'Inactive' ReplaceableAttribute testAttribute = new ReplaceableAttribute(); testAttribute.setName("Name"); testAttribute.setValue("InactiveValue"); // This should not throw even though we already have the max number of // attributes. optimisticPersister.put(testItemName, Optional.of(42), testAttribute); }
private void doTestPutUsesCorrectVersionWhenCallingTheDatabase(Optional<Integer> version) throws Exception { // ARRANGE initialiseOptimisticPersister(); // Configure attributes for database to return - the get is used for logging // only, so does not really matter. GetAttributesRequest simpleDBRequest = new GetAttributesRequest(testSimpleDBDomainName, testItemName); simpleDBRequest.setConsistentRead(true); GetAttributesResult getAttributesResult = new GetAttributesResult(); mockery.checking(new Expectations() { { allowing(mockSimpleDBClient).getAttributes(with(equal(simpleDBRequest))); will(returnValue(getAttributesResult)); } }); // Configure expectations for the put: UpdateCondition updateCondition = new UpdateCondition(); updateCondition.setName(versionAttributeName); ReplaceableAttribute versionAttribute = new ReplaceableAttribute(); versionAttribute.setName(versionAttributeName); versionAttribute.setReplace(true); if (!version.isPresent()) { // A version number attribute did not exist - so it still should not updateCondition.setExists(false); // Set initial value for our version number attribute versionAttribute.setValue("0"); } else { // A version number attribute exists - so it should be unchanged updateCondition.setValue(Integer.toString(version.get())); // Bump up our version number attribute versionAttribute.setValue(Integer.toString(version.get() + 1)); } List<ReplaceableAttribute> replaceableAttributes = new ArrayList<>(); replaceableAttributes.add(versionAttribute); // Add the new attribute ReplaceableAttribute testAttribute = new ReplaceableAttribute(); testAttribute.setName("Name"); testAttribute.setValue("Value"); replaceableAttributes.add(testAttribute); PutAttributesRequest simpleDBPutRequest = new PutAttributesRequest(testSimpleDBDomainName, testItemName, replaceableAttributes, updateCondition); mockery.checking(new Expectations() { { oneOf(mockSimpleDBClient).putAttributes(with(equal(simpleDBPutRequest))); } }); // ACT optimisticPersister.put(testItemName, version, testAttribute); }
private void doTestPutHandlesExceptionsCorrectly(Boolean isConditionalCheckFailedException) throws Exception { // The persister should forward all simpleDB exceptions to us, but it should // convert ConditionalCheckFailed exceptions before forwarding, as clients // will likely want to handle that case differently. // ARRANGE thrown.expect(Exception.class); thrown.expectMessage(isConditionalCheckFailedException ? "Database put failed" : "Boom!"); initialiseOptimisticPersister(); // Configure attributes for database to return - the get is used for logging // only, so does not really matter. GetAttributesRequest simpleDBRequest = new GetAttributesRequest(testSimpleDBDomainName, testItemName); simpleDBRequest.setConsistentRead(true); GetAttributesResult getAttributesResult = new GetAttributesResult(); mockery.checking(new Expectations() { { allowing(mockSimpleDBClient).getAttributes(with(equal(simpleDBRequest))); will(returnValue(getAttributesResult)); } }); // Make the simpleDB call throw the correct exception AmazonServiceException exception = new AmazonServiceException("Boom!"); exception.setErrorCode(isConditionalCheckFailedException ? "ConditionalCheckFailed" : "SomeOtherArbitraryCode"); mockery.checking(new Expectations() { { oneOf(mockSimpleDBClient).putAttributes(with(anything())); will(throwException(exception)); } }); ReplaceableAttribute testAttribute = new ReplaceableAttribute(); testAttribute.setName("Name"); testAttribute.setValue("Value"); // ACT optimisticPersister.put(testItemName, Optional.of(42), testAttribute); }
@Test public void testPutReturnsCorrectVersion() throws Exception { // A successful put should return the version that the put-to item has after // the put - i.e. one higher than it had initially. // ARRANGE initialiseOptimisticPersister(); // Configure attributes for database to return - the get is used for logging // only, so does not really matter. GetAttributesRequest simpleDBRequest = new GetAttributesRequest(testSimpleDBDomainName, testItemName); simpleDBRequest.setConsistentRead(true); GetAttributesResult getAttributesResult = new GetAttributesResult(); mockery.checking(new Expectations() { { allowing(mockSimpleDBClient).getAttributes(with(equal(simpleDBRequest))); will(returnValue(getAttributesResult)); } }); mockery.checking(new Expectations() { { allowing(mockSimpleDBClient).putAttributes(with(anything())); } }); ReplaceableAttribute testAttribute = new ReplaceableAttribute(); testAttribute.setName("Name"); testAttribute.setValue("Value"); int initialVersion = 42; // Arbitrary // ACT int finalVersion = optimisticPersister.put(testItemName, Optional.of(initialVersion), testAttribute); // ASSERT assertTrue("The returned version should be one higher than the initial version", finalVersion == (initialVersion + 1)); }
@SuppressWarnings("unchecked") protected Collection<ReplaceableAttribute> determineReplaceableAttributes() { return exchange.getIn().getHeader(SdbConstants.REPLACEABLE_ATTRIBUTES, Collection.class); }
private void addAttribute(List<ReplaceableAttribute> attributes, String key, String value) { if (value == null) { value = ""; } attributes.add(new ReplaceableAttribute().withName(key).withValue(value)); }
@Override public void flush() { if (cache.size() < 1) { return; } BatchPutAttributesRequest req = new BatchPutAttributesRequest(); List<ReplaceableItem> items = new ArrayList<ReplaceableItem>(CACHE_SIZE); for (Map.Entry<String, Map<String, String>> cacheEntry : cache .entrySet()) { ReplaceableItem entry = new ReplaceableItem(cacheEntry.getKey()); List<ReplaceableAttribute> attributes = new ArrayList<ReplaceableAttribute>( cacheEntry.getValue().size()); for (Map.Entry<String, String> dataEntry : cacheEntry.getValue() .entrySet()) { attributes.add(new ReplaceableAttribute(dataEntry.getKey(), dataEntry.getValue(), false)); } entry.setAttributes(attributes); items.add(entry); } req.setDomainName(domain); req.setItems(items); int tries = 0; do { tries++; try { client.batchPutAttributes(req); cache.clear(); return; } catch (Exception ase) { log.warn(ase); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } while (tries < MAX_TRIES); cache.clear(); throw new RuntimeException("Unable to connect to SDB " + domain); }
public static void createItem(String domainName, String itemName) { List<ReplaceableAttribute> attributes = new ArrayList<ReplaceableAttribute>(1); attributes.add(new ReplaceableAttribute().withName("Name").withValue("Value")); getInstance().putAttributes(new PutAttributesRequest(domainName, itemName, attributes)); }
public static void createAttributeForItem(String domainName, String itemName, String attributeName, String attributeValue) { List<ReplaceableAttribute> attributes = new ArrayList<ReplaceableAttribute>(1); attributes.add(new ReplaceableAttribute().withName(attributeName).withValue(attributeValue).withReplace(true)); getInstance().putAttributes(new PutAttributesRequest(domainName, itemName, attributes)); }
/** * Add a quote to the Quotes Table * * @param quote quote to be added to the table */ public static void addQuote(QuotePost quote) { ReplaceableAttribute quoteAttribute = new ReplaceableAttribute("quoteText", quote.getQuoteText(), Boolean.FALSE); ReplaceableAttribute authorAttribute = new ReplaceableAttribute("author", quote.getAuthorName(), Boolean.FALSE); ReplaceableAttribute timeAttribute = new ReplaceableAttribute("timestamp", "" + quote.getTimestamp(), Boolean.FALSE); ReplaceableAttribute fbNameAttribute = new ReplaceableAttribute("fbName", quote.getFbName(), Boolean.FALSE); ReplaceableAttribute fbIdAttribute = new ReplaceableAttribute("userId", quote.getUserId(), Boolean.FALSE); ReplaceableAttribute numFavorites = new ReplaceableAttribute("favorites", "0", Boolean.TRUE); Integer[] categories = quote.getCategories(); List<ReplaceableAttribute> attrs = new ArrayList<ReplaceableAttribute>(6 + categories.length); attrs.add(quoteAttribute); attrs.add(authorAttribute); attrs.add(timeAttribute); attrs.add(fbNameAttribute); attrs.add(fbIdAttribute); attrs.add(numFavorites); //add every category to the attribute - can have multiple values for (int i = 0; i < categories.length; i++) { switch (categories[i]) { case 0: attrs.add(new ReplaceableAttribute("category", "advice", Boolean.FALSE)); break; case 1: attrs.add(new ReplaceableAttribute("category", "funny", Boolean.FALSE)); break; case 2: attrs.add(new ReplaceableAttribute("category", "inspirational", Boolean.FALSE)); break; case 3: attrs.add(new ReplaceableAttribute("category", "love", Boolean.FALSE)); break; case 4: attrs.add(new ReplaceableAttribute("category", "movie", Boolean.FALSE)); break; case 5: attrs.add(new ReplaceableAttribute("category", "song", Boolean.FALSE)); break; default: throw new IllegalArgumentException("Too many categories."); } } PutAttributesRequest par = new PutAttributesRequest(QUOTES, quote.getFbName().replace(" ", "") + quote.getTimestamp(), attrs); try { getInstance().putAttributes(par); } catch (Exception exception) { System.out.println("EXCEPTION = " + exception); } }
private static ReplaceableAttribute attribute(String key, String value, boolean replaceable) { return new ReplaceableAttribute(key, value, replaceable); }