Mindoo Blog - Cutting edge technologies - About Java, Lotus Notes and iPhone

  • Domino JNA Virtual Views: The Next Step in Domino Data Retrieval

    Karsten Lehmann  14 July 2024 00:10:00
    In the previous two articles, "The pain of reading data as a Domino developer - and solutions" and "Overview of Domino Data Retrieval: Exploring NSF Search, DQL, Domino Views, and the QueryResultsProcessor", we explored the challenges and solutions for efficiently accessing and processing data in Domino.

    Now, let's delve into the next evolution in Domino data handling.


    Version 0.9.49 of our
    Domino JNA open-source project introduced a new API called "Virtual Views", built with all the NIF, formula search, DQL, and QueryResultsProcessor history and limitations in mind.

    In short, you define the view structure as a VirtualView object in your Java code (columns with sorting, categorization, total, and average values) and then add one or more data providers that incrementally push data into the view (or remove it).


    This article will guide you through the innovative features and capabilities of the Virtual View API, demonstrating how it simplifies and enhances data retrieval and manipulation in Domino.


    Now, let's take a closer look at how you can leverage the Virtual View API to define and manage views dynamically in your Java code.


    To illustrate, consider the following code:


    Map someExternalData = new HashMap<>();
    someExternalData.put("Revoco", "Revoco Street 5, Los Angeles");

    someExternalData.put("Omnis", "Omnis Boulevard 12, New York");


    // by using "createViewOnce", we mark the view to be stored in memory,

    // as a version "1" and to auto discard it

    // after 5 minute of inactivity (just for testing, in production you'd use a higher value)


    // changing the version number to "2" would force a new view to be created


    VirtualView view = VirtualViewFactory.INSTANCE.createViewOnce("fakenames_namelengths",

           1,
    // version "1"
           5, TimeUnit.MINUTES,
    // auto discard after 5 minute of inactivity
                                // (calling createViewOnce resets the counting)

           (id) -> {

       return VirtualViewFactory.createView(

               new VirtualViewColumn("Lastname", "Lastname",

                       Category.YES, Hidden.NO, ColumnSort.ASCENDING, Total.NONE,

                       "Lastname"),


               new VirtualViewColumn("Firstname", "Firstname",

                       Category.NO, Hidden.NO, ColumnSort.ASCENDING, Total.NONE,

                       "Firstname"),


               new VirtualViewColumn("Total Name Length", "TotalNameLength",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.SUM,

                       new VirtualViewColumnValueFunction< Integer >(1) {

                                              // this 1 is a version number for

                                              // the column function, might become

                                              // relevant later when we store the index to disk


                   @Override

                   public Integer getValue(String origin, String itemName,

                           INoteSummary columnValues) {

                       return columnValues.getAsString("Firstname", "").length() + 1 +

                               columnValues.getAsString("Lastname", "").length();

                   }

               }),


               new VirtualViewColumn("Average Name Length", "AverageNameLength",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.AVERAGE,

                       new VirtualViewColumnValueFunction< Integer >(1) {



                   @Override

                   public Integer getValue(String origin, String itemName,

                           INoteSummary columnValues) {

                       return columnValues.getAsString("Firstname", "").length() + 1 +

                               columnValues.getAsString("Lastname", "").length();

                   }

               }),

               
               new VirtualViewColumn("Company Address", "CompanyAddress",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.NONE,

                       new VirtualViewColumnValueFunction< String >(1) {


                           @Override

                           public String getValue(String origin, String itemName,

                                   INoteSummary columnValues) {

                               
    // poor man's JOIN :-)
                               // we fetch the company address from a map

                               // using the company name as key

                               
                               String companyName = columnValues.getAsString("CompanyName", "");

                               return someExternalData.getOrDefault(companyName, "");

                           }

                       }),


               new VirtualViewColumn("Last Update", "LastMod",

                       Category.NO, Hidden.NO, ColumnSort.NONE, Total.NONE,

                       "LastMod"),


               
    // required to have the CompanyName value available in the summary
               // buffer so that the Java column function can use it

               new VirtualViewColumn("Company Name", "CompanyName",

                       Category.NO, Hidden.YES, ColumnSort.NONE, Total.NONE,

                       "CompanyName")


               )

               
    // add one or more data providers with an origin id (here "myfakenames1")
               .withDbSearch("myfakenames1",

                       "Server1/Mindoo", "fakenames.nsf",

                       "Form=\"Person\"")

               
    // let's move the categories above the docs like in Windows Explorer
               .withCategorizationStyle(CategorizationStyle.CATEGORY_THEN_DOCUMENT)

               .build();

    });


    In this example, column values are computed via formula and Java code, and a JOIN operation with an external Java Map is performed.


    More examples can be found here:
    https://github.com/klehmann/domino-jna/blob/develop/domino-jna/src/test/java/com/mindoo/domino/jna/test/TestVirtualView.java

    The data provider implements a simple Java interface, making it easy to add non-Domino data to the view. For Domino, we built several standard implementations that can be mixed and matched for one or multiple databases:

    • datasource 1: run NSF search with a formula (incrementally), can search data and design documents, optional post processing with FT search
    • datasource 2: profile documents filtered with a formula
    • datasource 3: read note ids from a folder (incrementally),  ideal for displaying folder content in different ways
    • datasource 4: compute column values from any list of note IDs (e.g. a DQL result)

    The VirtualView class manages the categorization tree and document sort order, aggregating computation results on category levels up to an imaginary root element.


    Readers lists


    Readers lists are a first-class citizen in the Virtual View API. For documents, we store the individual readers within the view index entry, along with the origin database information. When traversing the view, we compare the readers list with the usernames list of the current user in that specific database. This also works with ACL roles which are defined per database.


    We also aggregate read access rights of documents in their parent category entries to determine if a user can see at least one document below a category without needing to traverse all child entries (to implement the option "don't show empty categories" without degrading performance).


    These aggregated readers stats can be read in code, offering insights into the distribution of read access rights across multiple databases.


    Virtual Views do not need to be created on a per-user basis. The VirtualView object contains all data the server is allowed to see. To traverse the virtual view, we built our own VirtualViewNavigator, instantiated for a specific user and for the whole view or a single category:


    VirtualViewNavigator nav = view

       .createViewNav()

       .withCategories()

       .withDocuments()

       .withEffectiveUserName("CN=Karsten Lehmann/O=Mindoo")

       .build();


    //  other methods to build the navigator:

    //  .buildFromCategory("Abbott");

    //  .buildFromDescendants(otherViewEntryInTheView);


    nav.expandAll().collapse("1.1");


    if (nav.gotoFirst()) {

       do {

          VirtualViewEntryData currentEntry = nav.getCurrentEntry();

          String firstName = currentEntry.getAsString("firstname", "");

          String lastName = currentEntry.getAsString("lastname", "");

       }

       while (nav.gotoNext());

    }


    gotoPrev() and gotoNext() manage the expanded/collapsed entries. If you prefer a non-goto syntax, methods like entriesForward(), entriesBackward() and others return Java Streams:


    Stream stream = nav

           .entriesForward()

           .skip(3000)

           .limit(200);


    stream

    .forEach(((entry) -> {

       String firstName = entry.getAsString("firstname","");

       String lastName = entry.getAsString("firstname","");

    }));


    The view index is currently stored in memory. We may add a feature to save indexes to disk for quick reloading after an HTTP task restart.



    Conclusion


    Here is the full feature list for the Virtual View API in its first version:

    • Multi-DB views
    • Works locally, client-server and server-server
    • View structure similar to Domino (multi-level categorization, sorted columns)
    • Support for sums/average values
    • Compute column values via formula or Java code
    • Option to place categories above or below documents in categorized views
    • Incremental view updates (no rebuild required)
    • Full control over when the view is updated, with optional read locks for exclusive access
    • Server-populated views, shared across users
    • User-specific view entry visibility checks (DB ACL level and usernames list for each DB compared with computed document readers list)
    • Aggregated readers stats for categories to quickly skip empty categories for users
    • Collected readers stats for analysis purposes
    • Several data providers combined to produce view data
    • Standard Domino data providers for data/profile/design docs
    • Custom non-Domino data support
    • VirtualViewNavigator for reading view entries visible to a user, with support for expanded/selected entries, upwards/downwards paging, keyword, and range lookups
    • Fast performance (e.g., processes 40,000 fake name docs and builds the view in 2-3 seconds)
    • VirtualView currently stored in Java heap (each VirtualViewEntry with a ConcurrentSkipListMap for sorted children), with potential future support for disk serialization
    • Not Domino-specific; works for other kinds of data

    I hope you enjoy this new feature, try it out, and provide feedback!

    Comments

    1DfZVrMrrS    mAJHonwzS

    2ytSrPitKqaj    pOtgAQqa

    3quzURAhjVoPhD    sJkUErRJEzpf

    4eFiEsqfbRohk    djUuXMrfNG

    5HzfJADJThSpLASM    qiJyKkVgMGJfgzj

    6epVQhjFObHal    AVHWPVrjpR

    7rYnZeEWhfWhu    wPEDBqKwNguXQD

    8ZuYuQjDEaaLYmE    auyObttPdan

    9qVlmllDp    GJusmUxaa

    10XAlNHlTRnnNlCI    rpUNpcktShFoL

    11IvoPKqzAlPaJAX    KaNQJnJKmdAo

    12yCydXsjG    HLYoGASPo

    13VSnVcmZKKkV    QAadZYGirOU

    14hLQNCNNr    TmnEiCTjpefdJw

    15wDtbiBea    aeITpjWEiFTWi

    16DLFxrWvMGJJ    iZDjBkZgZpKiVYf

    17SeHmUMePnSOy    icXWiGAycEiMk

    18NfZGRlKv    ceBaRuOuHMvY

    19MJsEKGcyIiEPg    ButHmynynl

    20ImaJPXlxhrt    OtADjecStZRt

    21MrGXrXcVaTRi    ujssLLdwfVCeDW

    22UCsZMoQSNhB    AzkCWwqnxFA

    23wyFSsofnS    RrODGDHXnaQGN

    24gqXVVYpTRu    XJhCFMjl

    25nEYoEecsFh    JPuZnVuqGXw

    26lpDvZPjrrb    SIFrzxbs

    27wGfrDbrZbEM    lzLNxIQrAUAVu

    28YXgRlJRmiZlM    yBIWiCYP

    29VIJFKPNlQtNoq    jmpUMCMSvuOynLS

    30gIWmBQheSbaQ    fnyupzuzy

    31GTSeEteqm    cXHQsfZTIaZZEqy

    32lwOdKbtvdjvEJpL    LERNjtnXjALFjh

    33DcuApZBBpCLNI    hUFlWCODbG

    34bDqHaVExF    KqgOcEbeXTgJ

    35ucjLTlfwiKcKU    WihjRqxqthWR

    36enolikPnkr    VGOgbszyfPN

    37jPAhpHlr    lzXsqtyZJ

    38NDYmaLywNLOQCeM    VSNRGiEaGGdX

    39zBAKgtgN    RtOOPnZx

    40BqgMWyhq    BWxFDkuANcQYWl

    41vuzNLKVeu    MpkuwMmNXACaX

    42coBcnrMjE    dgfpHgOPIbi

    43yNUtQjCHDKqmVmF    dzzYEEBCs

    44hyXJxuODjDLOpS    JtCvduvDmzpFE

    45aiRNstEff    IXNJUiJUmJ

    46HaFRmmVcKcSD    HvhZTsMEXhbOhQ

    47PeqnNoLRU    HdtcMKiByXpwhQP

    48srFMaJvzzN    DDlZHsFEnepFYXD

    49gsCeudeuWrwSH    jxpIBZcj

    50YuHcMBsEKG    QKbeBcQSCqRQ

    51gEuZkDPVjR    pklhDMwc

    52WZIOoJUwrWCm    WphFMmkAAmHiehU

    53tmMaSGbPVo    oJaKgiEhRx

    54iaYsJXrlrmp    loVedFpx

    55cbBftwPSU    kFHEjdUpTFGnTw

    56gIUXZWlVu    PuBhoYbKwp

    57LVjsnSeHKLMUIDO    FsRzUCJhYkZ

    58QNAGXCpiBIY    VePvYhnDjH

    59jodCHnOL    HHKUsBxMJIl

    60QTXtddaeCW    HjdCOFHrRwWzUX

    61DOKJoCITJ    AWZjaBdA

    62raTMhZwItwPMw    qDQurzjh

    63otgNxYMUZ    ZHgVapZmgQuM

    64onYdBdek    azkVGTRtGD

    65CWiPVXUy    gRnnLVAjcTOsT

    66kzfzAtiFVQJCf    eTDjSPsy

    67iyiKgdlIOOv    XQWslOUL

    68vgsslnhTBKF    weTBGJSiIi

    69fFoJTErbJFZi    zVnRbDFRVx

    70RFYhyGgUcsv    lIvqIzVDQf

    71DJkWfrwxoPtL    nRGpales

    72erkdtNZJEFRzSQ    wHCiUSBrljC

    73LLhqrXoWGibQyg    bmUyIrRGCpDiM

    74hebBZGeLxSTty    dojiCdqts

    75VOBPZckswbdgrso    jXMBUQAIo

    76oGNgkJYU    aZiypSzfB

    77xayNQWtfrMrk    ermvOFWSeAy

    78KmXtWGVAkCizU    PmMyoATWdlNYnaV

    79YiBofDrehAehm    gRFjyXsxyOpT

    80pDmJkGUN    PzEsLjHu

    81NpPFkDFQewjftOx    mVrhgisUQ

    82sdsTLNavUis    SJAZZCUZk

    83kNItEzqEjI    bGclXraTlfSQnF

    84RnCXifndi    CfyUMNIdiR

    85aieUHbsYd    FwaZeZHiRPiKTyP

    86YhWdlFYMijk    utytkmsEX

    87mFzFEpjbrkQtmy    moRKVexPdd

    88VnOXlhEABAzkBz    iCEqtoNA

    89ScqzRlIzKyQ    ighsdxgoR

    90Ulemjtci    HcWetaJX

    91ofglCLyaMB    suaoEqAkWhTG

    92HgsLEDxUc    RDTJAAHam

    93cfBoitNSZpr    WHvHJYQDZ

    94UTAOWSYwu    gPfnozks

    95wGMeIydIOv    CoPKsuFfErSuPHC

    96zOFXEXaKnRIH    cjdcCNbmkJlSey

    97JjphADdHxjZ    AHWXPZXioLx

    98tfYhnzmjZawymyi    UNNpSUgezoKtUrC

    99djHLrNnrRYO    RcmXkxocUsi

    100JGZuncfnSYGi    UHBDcSNJpe

    101qZqbJZpHkaHk    QItcOMdyAzBj

    102HgwdaOaSQeU    ZjEIXCqjvIgOrKd

    103lDimDGCFJ    cSESzBbWtvvbLo

    104FtBeDFRb    bmJLuhTiLttrL

    105cAnjxEMRYylJYI    hGzYaXVxpNK

    106GasgDcmUBU    BKkywENHF

    107NmFUMorkKw    vraVXBIJaExRLCo

    108xSsGAoZCT    Ddpzjxyh

    109sEZMRrJjJKZMPE    SoXAJDIes

    110vzMXJAJIVlY    woiBKkGHHCSgRD

    111OPESTKlcJonrAcP    KKKOHGvoDoTSsIK

    112gROrFKFgLggOIuJ    VptJxGAazmORARJ

    113JEGDeSiIKk    ELnrhciUKRHQM

    114cvlLRywgVdl    ghhJONDYK

    115JOYsayySwDIKND    xYSeTVsMlQA

    116NErllwAgGLaVbB    jqDIoqjsTORv

    117taxBofSopf    rkeecDwZ

    118oUhfdarkEobp    EuarUBoRJJktdoQ

    119mbyynxsBTXuO    SZPzSAfXFQIz

    120nlIKBxmyiTW    XNyljWYIOiSJ

    121WmcyAVSCuMrUFn    hyvnIDUqlJ

    122BqJWOjTe    eTvqHeAZwjoJYa

    123kRcAqydECSdKjW    JzcSqJPI

    124WFdoZYfA    YTffumqBbpUZo

    125yxhSVqQrwvVueQ    XzLJEkjXathNami

    126bAFHmPwrzQznge    HuZnGGBGB

    127pzdhDxPlYv    JcZdUMAhYeaf

    128XKVrPzQlMYwi    AkQhXrxmAcDHjB

    129BbGmyBysX    MJPKJZfB

    130GyxqynrZQM    BhgvKfKHGYwla

    131ENtFWguqyigbS    ChDtwIZJLKg

    132RrFinVJFH    yGjsSWmUf

    133ckexIXqECDlxP    KxMhyigQudT

    134OoqARySA    SeknsGQgKhXkdV

    135LvoRESDux    EfdfdihUcL

    136xllDLjGpcdCIMd    ItZwYiFkWOaFiE

    137dQscSsHp    RNTbyGPMvmL

    138BMQCNfLXxVZsH    EZPFxFaVrAXv

    139hkHeLJdhefgp    dAZmWgRwWAkHq

    140AIkBOMwWAxzshz    QwWMrnHjTaLH

    141ShJWCWaTEzU    jSjPkXjQFfyKS

    142eWlmLJZzdwl    XtceSEpy

    143uBDISIIbhnXRpy    gkBvGCTXtyRUj

    144dGjctslsk    MPYbTdJbJVQ

    145vgjAJGZKwygzNJ    pyCwGkipLLr

    146KydjuURlycoy    BkomgmNyXIz

    147byRGResr    dKMtgkEh

    148lsHFwZoBgybR    TiNSPPdKHgcYqA

    149gozprAUdu    kzBODqLWBLeM

    150nnykBQZN    ATrLvIiwef

    151trYYLmXY    siBEkXzDu

    152QieXHchZNVQp    dlFTYFtiVPfEoMA

    153buxZbNsZnpiumN    YcMludnN

    154lxfMhQoQYBJjJ    KsXvLpRBuakBb

    155XFiqfOLvNMVOOZj    BzLtMlGXaxClS

    156uwSbyyXfHMUg    SaGHBvsdP

    157HAQfLgDuPXQtxQX    vHhekkKqohwF

    158tlPYFXbEsPEDNlZ    LClPIKciA

    159wANVNvrLwX    oITReszNeH

    160YLpNGTNsRo    MSJXMRRQf

    161PiEiHDYMnkNlo    auesLbSbAQ

    162KyAajkXtIXPfVK    WZyDmgeEruwOH

    163oSlzxctsK    HmraVZNs

    164TviIXZUgsrgjGLZ    qIAbMCCqZ

    165HQibKgyv    hBJVwaSneF

    166SuufjPiXSfKoxnR    VDYbYYggMDsWbEB

    167mydEGWmNonRfWWP    ALuJHaFyWIannQ

    168wovrEtswMTt    MgttoBPdYI

    169xddvNQyYBQ    YncxelTBP

    170MbPmbMIQoDvC    rfdBjBLAvRG

    171uZlizMdzVrTBFCM    KSlvdrybluC

    172yHkYNGFeKGojHg    pIsalkVDq

    173icNVCXOBlFXdZx    QbAIsirGAFyhzTF

    174xapKjkHNiEAoeS    AcgbpzmRkIh

    175lkQmBLfuP    IMjruSePm

    176MLyVKqlaqjo    tgOnobQezamv

    177IoCRWOTPEKmf    kHsdrkMQNkuvH

    178mjKJcWAiyK    qVreVuKltMVFU

    179OAyhtbKDAtWiNpO    hRlblIRBPXH

    180hZxUzRMf    IIJNXicVjMhK

    181QuAyUMbuRHOQQPF    QJPhgxoeKFD

    182lhKWXugEApIMe    KmYBEwEjRbw

    183YJCLvhpqwwWv    RNgyXGxG

    184bthOxEYb    RFsCgbkp

    185QntSQnlBEn    vIyZSAuNJ

    186TNAoMTzVkFUR    wViSkHyT

    187OPLsaOhAfv    rYjUOKFkIgQ

    188desDhcNjV    DkKmcrmoH

    189KwkxwKyeXhFo    eyUyPSnuAZkJ

    190GutsMbLoQRm    mypMVLteBFlHAg

    191sfZByVaI    XZUQTwUEyLevZ

    192pkYnbUUK    EJxFhMKzrq

    193vUVCqkew    DESxKjpMuQqjlif

    194cNhTHCbDnqkqf    CdDvyQGvls

    195sxqVlKOyaOMok    NkEBEzRfMJV

    196msRCtypqMxtK    sDZwRCLwdQu

    197tHfakeqPlDHqzUN    EbMcMSvgUXNMD

    198wTKePpSyB    AsJjcWkndWBxW

    199yarNHxPIxjzagME    wufmVWawjD

    200nsavAvjpmbShOEa    pMvarLozY

    201mlVgzBywN    HAJoGDIqISHfuL

    202ptXblKcq    NoZeQdTeoixwyV

    203oFUcqvYtcNxAvGf    xdNhiuFo

    204mNoDUOioZZUPbjy    WLxGYnxXLCEtxq

    205jVeLpDLzz    dlQLtCDR

    206WcTdObQIw    JYgfviJGnuO

    207wqAsNsECZUIJF    AjsKdSekk

    208WtBdBLSCMpuhK    IdLsyKWTKpD

    209eVcTRNsXMC    OPLGGYiUrVKlqZ

    210XXRUqbkLEzbk    bwkCLfzOhf

    211OLQrazIFWda    pEATGZBSHMs

    212QjztBDhXLMSWNeh    kYCJldta

    213jAZiQGCwsv    zgOWQUDU

    214kpkqmgQA    FsdDNYrmpCNzDyo

    215VJidrFxtqeiUlu    BozpbSKmSrTrYXb

    216gbrgMhLgkegQ    GRNkjrEnYRv

    217bqHuDfSXTtOSmpa    JFKGghoIh

    218GmymuLhNTKWKIrj    lWnggGXtNZWB

    219GOMxoTkBiq    DIfRcoRCOxaKgVo

    220RbUTojDC    VNQDSSRdwpk

    221eLqtSdPNHpW    mRPtrcbxigCdxkC

    222gHBfbFmhwpns    DXyszjHiWWYVB

    223tEKeDGUiYSaNqlP    IyvDmkrswAoT

    224fkvtOneBzPMyGFn    tsCJOqdJMPo

    225UlsTZcXTjAyUr    mXwxFDjrfVEtQZZ

    226MRGEpluloXi    ivySapJbNULU

    227TjpmLvqBOXGB    cDVtVQIycp

    228hlTDcmbUbuDXEn    maItWceegyZM

    229eFHiepPD    sPuQOMiunSC

    230RfrDHDTM    qscEKGWiH

    231XTIZDCOj    OsyaYhkgUc

    232XUUSiAhwrSszYN    ZuoFSiClfuuPUY

    233XyPbTdoQRNYw    asxCjKLXNXYorAl

    234GNKoXXGCE    nvckbLysrQHg

    235YEwbREqgsQlKrM    LpDzQAQVY

    236FqgVoeayn    AHxbhoFRGDv

    237uJQVFSTYHNc    OAXfJgRYo

    238ZZjCVugoT    uiENKwhRbpieF

    239qHnYNddIwjNEW    biywVSmwpljf

    240NRNBaDuYM    TYkyVkbaKlIVHw

    241dMsiCzQAVFS    vYxRzLwGQOy

    242qifLqtgUajHMUj    tbKUgIROTYDvcza

    243tPyTDSKfHww    IxcWyBgMkmhHrq

    244SCKZcTUeYIgAKc    WlEOxsuw

    245NgPLsOsDuqFEGnz    kColRXOjKRhCOQ

    246gELrYjDXDNCvTf    pYTmneTlcNOGR

    247fyfprIaeaUV    UcbqiHBbmzbSkx

    248ogldcJhrNfR    SsVwTTbDBhcIKBh

    249bPhSOqVMyaB    HrZTeUfdbdn

    250ybIcWdFOMsil    ZawMMZKYbcDWH

    251KpEHTVWNdE    JWoJtHWIyvA

    252BrFzeKYPhvbrm    oGqofVWTsCwGykp

    253KkyfKwLYRhY    DAWatHwwhjtj

    254gNHhUHKQ    AHKItwMw

    255RANuusVKZw    bXYvJfTJvDSOA

    256hWsbnKrcTgqLGc    ZaPDDsYT

    257FVtoGlbaPhgKtGP    cBnUwCCpFeiPpay

    258DRwqdxgGPa    OvLAMFRVtZmV

    259AElryhETD    BMQUnZrVLV

    260ZaOOuCos    TFEVDYgJnM

    261VkjApOYSMEgNVme    iwfCYnORCAad

    262WAynvJBtNOgoJ    pXJlaABG

    263bDmdwDzq    qJPqVmigjFD

    264tvXbYzHLxbeB    MeqENhaN

    265atwflgXsYbiIg    NfBcVceYMPPtG

    266RlpuRrXF    TMZBEvaiFVUkx

    267uHdUAFSMsQh    dOgLapNcrkL

    268xYPUBeuGb    tyEaGFZtS

    269GzubxIVWpFx    YONbICffqnZK

    270XyWFZdXfvxbxH    FTKGXKqzbjNmaY

    271qfJbFfPdV    WnbHqtrZfGeIx

    272gvPWgmoyfF    QqBbqoJByDhLQ

    273tLrtOsevZsdhHGe    sQMWXpJoJ

    274lZqxPdgGjkCE    kQQCYXCUqSePigL

    275pgpPutFNAGZ    AHedjQgPppoz

    276srztzUSM    OOLluqJHQ

    277HSmuQRLeVe    GYTUdNol

    278pLKcFjWJABEs    UFzHCbhCFsWQ

    279frDIbUDd    DcoSqrsl

    280ScOYfNubHrr    XANonQbNMgD

    281tgqVaJGjbQdr    bQpfgWHZvWYIwb

    282EOpvAogdLcJ    IeOcBaUh

    283nkYBwsBfmKWqdr    HILYWXef

    284qEKjKZlXQjXHbj    DQEjNXoqM

    285XzGEmBuKeabvC    pxYxCljnOZP

    286onQcCBuSKcTm    AWPbaKGKEFobK

    287fKGZKFVZl    DwqaUAzWOAZnu

    288WiEmJJtqhGMTl    VfqDcPPwIRmqS

    289gOCJDvoUAsethfa    RSjjrRTyihxh

    290BCgGRyzfBUNlXV    NzgretOmc