Coverage Report - com.jcabi.dynamo.AwsIterator
 
Classes in this File Line Coverage Branch Coverage Complexity
AwsIterator
58%
27/46
16%
10/62
1.875
AwsIterator$AjcClosure1
100%
1/1
N/A
1.875
AwsIterator$AjcClosure3
100%
1/1
N/A
1.875
AwsIterator$AjcClosure5
0%
0/1
N/A
1.875
AwsIterator$Fixed
0%
0/8
N/A
1.875
 
 1  2
 /**
 2  
  * Copyright (c) 2012-2016, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.dynamo;
 31  
 
 32  
 import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
 33  
 import com.amazonaws.services.dynamodbv2.model.AttributeValue;
 34  
 import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
 35  
 import com.amazonaws.services.dynamodbv2.model.DeleteItemResult;
 36  
 import com.amazonaws.services.dynamodbv2.model.ReturnConsumedCapacity;
 37  
 import com.jcabi.aspects.Immutable;
 38  
 import com.jcabi.aspects.Loggable;
 39  
 import com.jcabi.immutable.Array;
 40  
 import com.jcabi.log.Logger;
 41  
 import java.io.IOException;
 42  
 import java.util.ArrayList;
 43  
 import java.util.Collection;
 44  
 import java.util.Collections;
 45  
 import java.util.Iterator;
 46  
 import java.util.List;
 47  
 import java.util.Map;
 48  
 import java.util.NoSuchElementException;
 49  
 import java.util.concurrent.atomic.AtomicReference;
 50  
 import lombok.EqualsAndHashCode;
 51  
 import lombok.ToString;
 52  
 
 53  
 /**
 54  
  * Iterator of items in AWS SDK.
 55  
  *
 56  
  * <p>The class is mutable and thread-safe.
 57  
  *
 58  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 59  
  * @version $Id: 16c947fa17d44229eadc7b833ef05559b1e7f89f $
 60  
  * @checkstyle ClassDataAbstractionCoupling (500 lines)
 61  
  * @since 0.1
 62  
  */
 63  3
 @Loggable(Loggable.DEBUG)
 64  0
 @ToString
 65  0
 @EqualsAndHashCode
 66  
     (
 67  
         of = { "credentials", "conditions", "frame", "name", "keys", "valve" }
 68  
     )
 69  
 final class AwsIterator implements Iterator<Item> {
 70  
 
 71  
     /**
 72  
      * AWS credentials.
 73  
      */
 74  
     private final transient Credentials credentials;
 75  
 
 76  
     /**
 77  
      * Conditions.
 78  
      */
 79  
     private final transient Conditions conditions;
 80  
 
 81  
     /**
 82  
      * Frame.
 83  
      */
 84  
     private final transient AwsFrame frame;
 85  
 
 86  
     /**
 87  
      * Table name.
 88  
      */
 89  
     private final transient String name;
 90  
 
 91  
     /**
 92  
      * List of primary keys in the table.
 93  
      */
 94  
     private final transient Collection<String> keys;
 95  
 
 96  
     /**
 97  
      * Valve that loads dosages of items.
 98  
      */
 99  
     private final transient Valve valve;
 100  
 
 101  
     /**
 102  
      * Last scan result (mutable).
 103  
      */
 104  
     private final transient AtomicReference<Dosage> dosage;
 105  
 
 106  
     /**
 107  
      * Position inside the scan result, last seen, starts with -1 (mutable).
 108  
      */
 109  
     private transient int position;
 110  
 
 111  
     /**
 112  
      * Public ctor.
 113  
      * @param creds Credentials
 114  
      * @param frm Frame object
 115  
      * @param label Table name
 116  
      * @param conds Conditions
 117  
      * @param primary Primary keys of the table
 118  
      * @param vlv Valve with items
 119  
      * @checkstyle ParameterNumber (5 lines)
 120  
      */
 121  
     AwsIterator(final Credentials creds, final AwsFrame frm,
 122  
         final String label, final Conditions conds,
 123  2
         final Collection<String> primary, final Valve vlv) {
 124  2
         this.credentials = creds;
 125  2
         this.frame = frm;
 126  2
         this.name = label;
 127  2
         this.conditions = conds;
 128  2
         this.keys = primary;
 129  2
         this.valve = vlv;
 130  2
         this.dosage = new AtomicReference<Dosage>();
 131  2
         this.position = -1;
 132  2
     }
 133  
 
 134  
     @Override
 135  
     public boolean hasNext() {
 136  30
         synchronized (this.dosage) {
 137  15
             if (this.dosage.get() == null) {
 138  
                 try {
 139  2
                     this.dosage.set(
 140  
                         this.valve.fetch(
 141  
                             this.credentials,
 142  
                             this.name,
 143  
                             this.conditions,
 144  
                             this.keys
 145  
                         )
 146  
                     );
 147  0
                 } catch (final IOException ex) {
 148  0
                     throw new IllegalStateException(ex);
 149  2
                 }
 150  2
                 this.position = -1;
 151  
             }
 152  15
             if (this.dosage.get().hasNext()
 153  
                 && this.position + 1 >= this.dosage.get().items().size()) {
 154  2
                 this.dosage.set(this.dosage.get().next());
 155  2
                 this.position = -1;
 156  
             }
 157  15
             return this.dosage.get().items().size() - this.position > 1;
 158  0
         }
 159  
     }
 160  
 
 161  
     @Override
 162  
     public Item next() {
 163  6
         synchronized (this.dosage) {
 164  3
             if (!this.hasNext()) {
 165  1
                 throw new NoSuchElementException(
 166  
                     String.format(
 167  
                         "no more items in the frame, position=%d",
 168  
                         this.position
 169  
                     )
 170  
                 );
 171  
             }
 172  2
             ++this.position;
 173  2
             return new AwsItem(
 174  
                 this.credentials,
 175  
                 this.frame,
 176  
                 this.name,
 177  
                 new Attributes(this.dosage.get().items().get(this.position)),
 178  
                 new Array<String>(this.keys)
 179  
             );
 180  1
         }
 181  
     }
 182  
 
 183  
     @Override
 184  
     @SuppressWarnings("PMD.UseConcurrentHashMap")
 185  
     public void remove() {
 186  0
         synchronized (this.dosage) {
 187  0
             final AmazonDynamoDB aws = this.credentials.aws();
 188  
             try {
 189  0
                 final Dosage prev = this.dosage.get();
 190  0
                 final List<Map<String, AttributeValue>> items =
 191  
                     new ArrayList<Map<String, AttributeValue>>(prev.items());
 192  0
                 final Map<String, AttributeValue> item =
 193  
                     items.remove(this.position);
 194  0
                 final long start = System.currentTimeMillis();
 195  0
                 final DeleteItemResult res = aws.deleteItem(
 196  
                     new DeleteItemRequest()
 197  
                         .withTableName(this.name)
 198  
                         .withKey(new Attributes(item).only(this.keys))
 199  
                         .withReturnConsumedCapacity(
 200  
                             ReturnConsumedCapacity.TOTAL
 201  
                         )
 202  
                         .withExpected(
 203  
                             new Attributes(item).only(this.keys).asKeys()
 204  
                         )
 205  
                 );
 206  0
                 this.dosage.set(new AwsIterator.Fixed(prev, items));
 207  0
                 --this.position;
 208  0
                 Logger.info(
 209  
                     this,
 210  
                     "#remove(): item #%d removed from DynamoDB, %s, in %[ms]s",
 211  
                     this.position,
 212  
                     new PrintableConsumedCapacity(
 213  
                         res.getConsumedCapacity()
 214  
                     ).print(),
 215  
                     System.currentTimeMillis() - start
 216  
                 );
 217  
             } finally {
 218  0
                 aws.shutdown();
 219  0
             }
 220  0
         }
 221  0
     }
 222  
 
 223  
     /**
 224  
      * Dosage with fixed list of items.
 225  
      */
 226  
     @Immutable
 227  
     private static final class Fixed implements Dosage {
 228  
         /**
 229  
          * List of items.
 230  
          */
 231  
         private final transient Array<Map<String, AttributeValue>> list;
 232  
         /**
 233  
          * Previous dosage.
 234  
          */
 235  
         private final transient Dosage prev;
 236  
         /**
 237  
          * Ctor.
 238  
          * @param dsg Dosage
 239  
          * @param items Items
 240  
          */
 241  0
         Fixed(final Dosage dsg, final List<Map<String, AttributeValue>> items) {
 242  0
             this.prev = dsg;
 243  0
             this.list = new Array<Map<String, AttributeValue>>(items);
 244  0
         }
 245  
         @Override
 246  
         public List<Map<String, AttributeValue>> items() {
 247  0
             return Collections.unmodifiableList(this.list);
 248  
         }
 249  
         @Override
 250  
         public Dosage next() {
 251  0
             return this.prev.next();
 252  
         }
 253  
         @Override
 254  
         public boolean hasNext() {
 255  0
             return this.prev.hasNext();
 256  
         }
 257  
     }
 258  
 }