Monday 24 October 2016

DynamoDB High Level API-DynamoDBMapper (Java)

DynamoDB provides a high level API DynamoDBMapper. This approach can reduce the amount of code comparing with low level APIs(putItem, getItemOutcome, updateItem, query and scan).

AWS has detailed document to explain how to use it, this article is focus on how to use it for custom object and list of custom object.

Before going to deeper, let's have a simple example first (From https://aws.amazon.com/blogs/developer/using-the-savebehavior-configuration-for-the-dynamodbmapper/ ).

Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#P", "Price");

@DynamoDBTable(tableName="TestTable")
public class TestTableItem {

   private int key;
   private String modeledScalar;
   private Set<String> modeledSet;

   @DynamoDBHashKey(attributeName="key")
   public int getKey() { return key; }
   public void setKey(int key) { this.key = key; }

   @DynamoDBAttribute(attributeName="modeled_scalar")
   public String getModeledScalar() { return modeledScalar; }
   public void setModeledScalar(String modeledScalar) { this.modeledScalar = modeledScalar; }
    
   @DynamoDBAttribute(attributeName="modeled_set")
   public Set<String> getModeledSet() { return modeledSet; }
   public void setModeledSet(Set<String> modeledSet) { this.modeledSet = modeledSet; }

}


The approach is simple, you just need to put the annotation to specify the property methods is DynamoDBHashKey, DynamoDBRangeKey or DynamoDBAttribute. If you don't need to save some property into database, you should put annotation DynamoDBIngore on top of property methods.

One thing to remember is the naming of your property methods, you must use the standard name start with "get" and "set". Don't use other prefix, otherwise DynamoDB mapper cannot "reflect" your attribute name to your methods.

To store a custom object of complex class, you need to use DynamoDBTypeConverter(DynamoDBMarshaller is deprecated already).
For example, we have a complex class PhoneNumber
public class PhoneNumber {
    private String areaCode;
    private String exchange;
    private String subscriberLineIdentifier;
 
    public String getAreaCode() { return areaCode; }  
    public void setAreaCode(String areaCode) { this.areaCode = areaCode; }
 
    public String getExchange() { return exchange; }
    public void setExchange(String exchange) { this.exchange = exchange; }
 
    public String getSubscriberLineIdentifier() { return subscriberLineIdentifier; }  
    public void setSubscriberLineIdentifier(String subscriberLineIdentifier) { this.subscriberLineIdentifier = subscriberLineIdentifier; }    
}


And we add a custom object to the above example.

@DynamoDBTable(tableName="TestTable")
public class TestTableItem {

   private int key;
   private String modeledScalar;
   private Set<String> modeledSet;
   private PhoneNumber phoneNumber;

   @DynamoDBHashKey(attributeName="key")
   public int getKey() { return key; }
   public void setKey(int key) { this.key = key; }

   @DynamoDBAttribute(attributeName="modeled_scalar")
   public String getModeledScalar() { return modeledScalar; }
   public void setModeledScalar(String modeledScalar) { this.modeledScalar = modeledScalar; }
    
   @DynamoDBAttribute(attributeName="modeled_set")
   public Set<String> getModeledSet() { return modeledSet; }
   public void setModeledSet(Set<String> modeledSet) { this.modeledSet = modeledSet; }

    @DynamoDBTypeConverted(converter = PhoneNumberConverter.class)
    public PhoneNumber getPhoneNumber() { return phoneNumber; }  
    public void setPhoneNumber(PhoneNumber phoneNumber) { this.phoneNumber = phoneNumber; }

}


Instead of using standard annotation DynamoDBAttribute, now we need to use @DynamoDBTypeConverted(converter = PhoneNumberConverter.class), and PhoneNumberConverter here is a class implementing DynamoDBTypeConverter interface.

Here is how the interface looks like. It just a interface to convert the object to a string and convert it back.
public class PhoneNumberConverterimplements DynamoDBTypeConverter<String, PhoneNumber>
{
    @Override
    public String convert(PhoneNumber number) {
        return "(" + number.getAreaCode() + ") " + number.getExchange() + "-" + number.getSubscriberLineIdentifier();
    }

    @Override
    public PhoneNumber unconvert(String s) {
        String[] areaCodeAndNumber = s.split(" ");
        String areaCode = areaCodeAndNumber[0].substring(1,4);
        String[] exchangeAndSlid = areaCodeAndNumber[1].split("-");
        PhoneNumber number = new PhoneNumber();
        number.setAreaCode(areaCode);
        number.setExchange(exchangeAndSlid[0]);
        number.setSubscriberLineIdentifier(exchangeAndSlid[1]);
        return number;
    }  
}


Having the above basic knowledge, now let's see how to do it for a List of custom object.
For example, we have a list of phone numbers.
@DynamoDBTable(tableName="TestTable")
public class TestTableItem {

   private int key;
   private String modeledScalar;
   private Set<String> modeledSet;
   private List<PhoneNumber> phoneNumbers;
 
   ......

   @DynamoDBTypeConverted(converter = PhoneNumberConverter.class)
   public List<PhoneNumber> getPhoneNumbers() { return phoneNumbers; }  
   public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) { this.phoneNumbers = phoneNumbers; }

}


What we need to do is to convert the list of object to string and convert it back.

public class PhoneNumberConverterimplements DynamoDBTypeConverter<String, PhoneNumber>
{
    private static final ObjectMapper mapper = new ObjectMapper();
    private static final ObjectWriter writer = mapper.writerWithType(new TypeReference<List<PhoneNumber>>(){});
    @Override
    public String convert(List<PhoneNumber> numbers) {
               try {
            return writer.writeValueAsString(numbers);
        } catch (JsonProcessingException e) {
            System.out.println(
                    "Unable to marshall the instance of " + numbers.getClass()
                    + "into a string");
            return null;
        }
    }

    @Override
    public List<PhoneNumber> unconvert(String s) {
        TypeReference<List<PhoneNumber>> type = new TypeReference<List<PhoneNumber>>() {};
        try {
            List<PhoneNumber> list = mapper.readValue(s, type);
            return list;
        } catch (Exception e) {
            System.out.println("Unable to unmarshall the string " + s
                             + "into " + s);
            return null;
        }
    }  
}


DynamoDB Low Level API-updateItem() (Java)

If you don't want to use high-level API DynamoDBMapper to update the DynamoDB table. You can use the low level APIs to update the table items.

AWS don't provide detail document for item update in Java, this document is going to introduce several item update methods systematically.

1. UpdateItemOutcome updateItem(PrimaryKey primaryKey, AttributeUpdate... attributeUpdates)

This is most simple one, you are using AttributeUpdate directly. By using AttributeUpdate, you can update the item by add objects by using addElements()or add number by using addNumeric().


1
2
3
4
5
6
7
final String phoneNumber = "9872-6530";
table.updateItem(HASH_KEY, ITEM_HASH_KEY, RANGE_KEY, ITEM_RANGE_KEY,
                new AttributeUpdate("phone").addElements(phoneNumber));
outcome = table.getItemOutcome(new GetItemSpec()
               .withPrimaryKey(HASH_KEY, ITEM_HASH_KEY, RANGE_KEY, ITEM_RANGE_KEY)
               .withConsistentRead(true));
item = outcome.getItem();


2. UpdateItemOutcome updateItem(UpdateItemSpec updateItemSpec)

This is most powerful one, UpdateItemSpec() has all the features you need to update a item. There is also a benefit of using UpdateItemSpec(), you can specify the return value and expected results. If the ReturnValue is UPDATED_NEW, then it only returns the attributes you updated.
If the ReturnValue is ALL_NEW, then it will returns the whole item with all new attributes.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
UpdateItemOutcome outcome = table.updateItem(new UpdateItemSpec()
            .withReturnValues(ReturnValue.ALL_NEW)
            .withPrimaryKey(HASH_KEY, ITEM_HASH_KEY, RANGE_KEY, ITEM_RANGE_KEY)
            .withAttributeUpdate(
                new AttributeUpdate("Player-Position").addNumeric(1),
                new AttributeUpdate("Status").put("SUSPENDED"))
            .withExpected(
                new Expected("Player1-Position").lt(20),
                new Expected("Player2-Position").lt(20),
                new Expected("Status").eq("IN_PROGRESS"))
        );


3. UpdateItemOutcome updateItem(PrimaryKey primaryKey,
            String updateExpression, String conditionExpression,
            Map nameMap, Map valueMap)

This is another method to update item. You can specify the update expression directly.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#P", "Price");

Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1", 25);  // update Price to 25...
expressionAttributeValues.put(":val2", 20);  //...but only if existing Price is 20

UpdateItemOutcome outcome = table.updateItem(
    new PrimaryKey("Id",101),
    "set #P = :val1", // UpdateExpression
    "#P = :val2",     // ConditionExpression
    expressionAttributeNames,
    expressionAttributeValues);


You can choose the method you need to update a DynamoDB table item.

Sunday 23 October 2016

Singapore Top News

Read Singapore popular newspapers in this one stop news reader, user can select the news from the following source:
    - The Straits Times
    - Today Online
    - Channel NewsAsia
    - MyPaper
    - Channel 8 News
    - The New Paper
    - Yahoo News Singapore
    - Lianhewanbao
    - OMY News
    - 大榴莲网
    - Stomp news
    - BBC
    - CNN
    - CNET
    - 文学城
    - 万维网
    - AsiaOne
    - LianHeZaoBao
    - Business Times
More newspapers are coming...

[Singapore Top News] is not a news parser only, we have the backend server to store the news. The loading speed of news is faster, you can also read the news even several days earlier.

It has day mode and night mode.


Welcome to report the issues to topnewssg@gmail.com.


Android screenshots

Supported sources:

News list:



 You can customise the news sources, delete the one you don't want.


You can check Singapore PSI also.

iOS Screenshots

Categorised the news in different sections from server, pull the news sorting by time.


Customise the sections based on your interest.