HaikuDepot: Process and Data-loading Improvements

This change is a reshuffle of the backend processing involved in the
aquisition of data from servers including the pull-down and load of
HPKR data as well as the pull-down and load of data from the
HaikuDepotServer (HDS) system.  The driver for this change is to
implement an initial implementation of a progress bar for the
loading of data as the application starts.

The following are notable changes;

* Removed some previously attempted 'functional style' logic in the
  model which didn't fit well with C++
* Use of the base-url in the logical mapping from HDS and HD data is no
  longer required and has been removed
* Some logging has been improved making it clearer which part of HD
  is producing the logging which in turn helps with debugging issues
* List class has been modified to more cleanly support sorted lists
  and binary searches; tests have also be updated accordingly
* Reorganise and tidy-up of the data-loading processes' structures
* The local repository update (HPKR) and data-load occur in background
  processes now in the same system as the HDS data-load - this has been
  crudely shifted from the MainWindow to new Processes and incorporated
  into the background processing system
* The 'state-machine' background process runner is now replaced with a
  'coordinator' style approach that can more easily handle the new
  processes related to HPKR loading.
* Progress for loading processes is shown in the main window in the
  WorkStatusView - this is flickering a bit, but basically works
* Added some documentation regarding how Processes work in the system
* The "Refresh Repositories" menu item now also updates data from HDS
* The "Refresh Repositories" menu item is disabled when the background
  processes are running that update the repository data

Some further refinement would be good, but this change is large enough
for one round of improvements.  There is an issue that the status bar
is used for screenshot display as well as this data-loading, but that
was the case before so it is something that can be dealt with later if
it is a problem.

Change-Id: I7668307645e3aabaf7e4a6e37e2cca80cc0f489e
Reviewed-on: https://review.haiku-os.org/770
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Andrew Lindesay
2018-12-17 19:31:25 +00:00
committed by waddlesplash
parent 1609bb2f67
commit 3369e03d5c
55 changed files with 3598 additions and 1733 deletions
@@ -0,0 +1,612 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="178.01mm" height="84.01mm" viewBox="1500 2000 17801 8401" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
<defs class="ClipPathGroup">
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
<rect x="1500" y="2000" width="17801" height="8401"/>
</clipPath>
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
<rect x="1517" y="2008" width="17766" height="8385"/>
</clipPath>
</defs>
<defs>
<font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="y" horiz-adv-x="1033" d="M 191,-425 C 142,-425 100,-421 67,-414 L 67,-279 C 92,-283 120,-285 151,-285 263,-285 352,-203 417,-38 L 434,5 5,1082 197,1082 425,484 C 428,475 432,464 437,451 442,438 457,394 482,320 507,246 521,205 523,196 L 593,393 830,1082 1020,1082 604,0 C 559,-115 518,-201 479,-258 440,-314 398,-356 351,-384 304,-411 250,-425 191,-425 Z"/>
<glyph unicode="w" horiz-adv-x="1509" d="M 1174,0 L 965,0 776,765 740,934 C 734,904 725,861 712,805 699,748 631,480 508,0 L 300,0 -3,1082 175,1082 358,347 C 363,331 377,265 401,149 L 418,223 644,1082 837,1082 1026,339 1072,149 1103,288 1308,1082 1484,1082 1174,0 Z"/>
<glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/>
<glyph unicode="s" horiz-adv-x="901" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
<glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
<glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/>
<glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/>
<glyph unicode="m" horiz-adv-x="1457" d="M 768,0 L 768,686 C 768,791 754,863 725,903 696,943 645,963 570,963 493,963 433,934 388,875 343,816 321,734 321,627 L 321,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 356,974 400,1027 450,1057 500,1087 561,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 967,970 1013,1022 1066,1054 1119,1086 1183,1102 1258,1102 1367,1102 1447,1072 1497,1013 1546,954 1571,856 1571,721 L 1571,0 1393,0 1393,686 C 1393,791 1379,863 1350,903 1321,943 1270,963 1195,963 1116,963 1055,934 1012,876 968,817 946,734 946,627 L 946,0 768,0 Z"/>
<glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
<glyph unicode="k" horiz-adv-x="901" d="M 816,0 L 450,494 318,385 318,0 138,0 138,1484 318,1484 318,557 793,1082 1004,1082 565,617 1027,0 816,0 Z"/>
<glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
<glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
<glyph unicode="d" horiz-adv-x="927" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 821,1035 821,1484 1001,1484 1001,223 C 1001,110 1003,36 1007,0 L 835,0 C 833,11 831,35 829,74 826,113 825,146 825,174 L 821,174 Z M 275,542 C 275,391 295,282 335,217 375,152 440,119 530,119 632,119 706,154 752,225 798,296 821,405 821,554 821,697 798,802 752,869 706,936 633,969 532,969 441,969 376,936 336,869 295,802 275,693 275,542 Z"/>
<glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/>
<glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
<glyph unicode="W" horiz-adv-x="1932" d="M 1511,0 L 1283,0 1039,895 C 1023,951 1000,1051 969,1196 952,1119 937,1054 925,1002 913,950 822,616 652,0 L 424,0 9,1409 208,1409 461,514 C 491,402 519,287 544,168 560,241 579,321 600,408 621,495 713,828 877,1409 L 1060,1409 1305,532 C 1342,389 1372,267 1393,168 L 1402,203 C 1420,280 1435,342 1446,391 1457,439 1551,778 1727,1409 L 1926,1409 1511,0 Z"/>
<glyph unicode="S" horiz-adv-x="1192" d="M 1272,389 C 1272,259 1221,158 1120,87 1018,16 875,-20 690,-20 347,-20 148,99 93,338 L 278,375 C 299,290 345,228 414,189 483,149 578,129 697,129 820,129 916,150 983,193 1050,235 1083,297 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 541,675 456,699 399,724 341,749 295,776 262,807 229,837 203,872 186,913 168,954 159,1000 159,1053 159,1174 205,1267 298,1332 390,1397 522,1430 694,1430 854,1430 976,1406 1061,1357 1146,1308 1205,1224 1239,1106 L 1051,1073 C 1030,1148 991,1202 933,1236 875,1269 795,1286 692,1286 579,1286 493,1267 434,1230 375,1193 345,1137 345,1063 345,1020 357,984 380,956 403,927 436,903 479,884 522,864 609,840 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z"/>
<glyph unicode="P" horiz-adv-x="1112" d="M 1258,985 C 1258,852 1215,746 1128,667 1041,588 922,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 919,1409 1041,1372 1128,1298 1215,1224 1258,1120 1258,985 Z M 1066,983 C 1066,1165 957,1256 738,1256 L 359,1256 359,700 746,700 C 959,700 1066,794 1066,983 Z"/>
<glyph unicode="N" horiz-adv-x="1165" d="M 1082,0 L 328,1200 333,1103 338,936 338,0 168,0 168,1409 390,1409 1152,201 C 1144,332 1140,426 1140,485 L 1140,1409 1312,1409 1312,0 1082,0 Z"/>
<glyph unicode="M" horiz-adv-x="1377" d="M 1366,0 L 1366,940 C 1366,1044 1369,1144 1375,1240 1342,1121 1313,1027 1287,960 L 923,0 789,0 420,960 364,1130 331,1240 334,1129 338,940 338,0 168,0 168,1409 419,1409 794,432 C 807,393 820,351 833,306 845,261 853,228 857,208 862,235 874,275 891,330 908,384 919,418 925,432 L 1293,1409 1538,1409 1538,0 1366,0 Z"/>
<glyph unicode="L" horiz-adv-x="927" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z"/>
<glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,574 1353,447 1296,338 1239,229 1159,145 1055,87 951,29 831,0 695,0 L 168,0 168,1409 634,1409 C 873,1409 1057,1349 1187,1230 1316,1110 1381,940 1381,719 Z M 1189,719 C 1189,894 1141,1027 1046,1119 950,1210 811,1256 630,1256 L 359,1256 359,153 673,153 C 776,153 867,176 946,221 1024,266 1084,332 1126,417 1168,502 1189,603 1189,719 Z"/>
<glyph unicode="C" horiz-adv-x="1324" d="M 792,1274 C 636,1274 515,1224 428,1124 341,1023 298,886 298,711 298,538 343,400 434,295 524,190 646,137 800,137 997,137 1146,235 1245,430 L 1401,352 C 1343,231 1262,138 1157,75 1052,12 930,-20 791,-20 649,-20 526,10 423,69 319,128 240,212 186,322 131,431 104,561 104,711 104,936 165,1112 286,1239 407,1366 575,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1174,1103 1122,1166 1050,1209 977,1252 891,1274 792,1274 Z"/>
<glyph unicode="B" horiz-adv-x="1112" d="M 1258,397 C 1258,272 1212,174 1121,105 1030,35 903,0 740,0 L 168,0 168,1409 680,1409 C 1011,1409 1176,1295 1176,1067 1176,984 1153,914 1106,857 1059,800 993,762 908,743 1020,730 1106,692 1167,631 1228,569 1258,491 1258,397 Z M 984,1044 C 984,1120 958,1174 906,1207 854,1240 779,1256 680,1256 L 359,1256 359,810 680,810 C 782,810 858,829 909,868 959,906 984,965 984,1044 Z M 1065,412 C 1065,578 948,661 715,661 L 359,661 359,153 730,153 C 847,153 932,175 985,218 1038,261 1065,326 1065,412 Z"/>
<glyph unicode="A" horiz-adv-x="1377" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 685,1265 L 676,1237 C 659,1182 635,1111 602,1024 L 422,561 949,561 768,1026 C 749,1072 731,1124 712,1182 L 685,1265 Z"/>
<glyph unicode=" " horiz-adv-x="556"/>
</font>
</defs>
<defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35"/>
</defs>
<defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
</g>
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
</g>
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
</g>
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
</g>
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
</g>
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
</g>
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
</g>
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
</g>
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
</g>
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
</g>
</defs>
<defs class="TextEmbeddedBitmaps"/>
<g class="SlideGroup">
<g>
<g id="container-id1">
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
<g class="Page">
<g class="com.sun.star.drawing.CustomShape">
<g id="id3">
<rect class="BoundingBox" stroke="none" fill="none" x="5499" y="3199" width="3003" height="3803"/>
<path fill="rgb(222,230,239)" stroke="none" d="M 7000,7000 L 5500,7000 5500,3200 8500,3200 8500,7000 7000,7000 Z"/>
<path fill="none" stroke="rgb(52,101,164)" d="M 7000,7000 L 5500,7000 5500,3200 8500,3200 8500,7000 7000,7000 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id4">
<rect class="BoundingBox" stroke="none" fill="none" x="5699" y="3399" width="2603" height="1003"/>
<path fill="rgb(180,199,220)" stroke="none" d="M 7000,4400 L 5700,4400 5700,3400 8300,3400 8300,4400 7000,4400 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7000,4400 L 5700,4400 5700,3400 8300,3400 8300,4400 7000,4400 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6209" y="4021"><tspan fill="rgb(0,0,0)" stroke="none">Process A</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id5">
<rect class="BoundingBox" stroke="none" fill="none" x="11999" y="5200" width="346" height="1602"/>
<path fill="none" stroke="rgb(18,118,34)" d="M 12000,6800 L 12247,5482"/>
<path fill="rgb(18,118,34)" stroke="none" d="M 12300,5200 L 12146,5476 12343,5513 12300,5200 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id6">
<rect class="BoundingBox" stroke="none" fill="none" x="5699" y="4599" width="2603" height="1003"/>
<path fill="rgb(180,199,220)" stroke="none" d="M 7000,5600 L 5700,5600 5700,4600 8300,4600 8300,5600 7000,5600 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7000,5600 L 5700,5600 5700,4600 8300,4600 8300,5600 7000,5600 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6198" y="5221"><tspan fill="rgb(0,0,0)" stroke="none">Process B</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id7">
<rect class="BoundingBox" stroke="none" fill="none" x="5699" y="5799" width="2603" height="1003"/>
<path fill="rgb(180,199,220)" stroke="none" d="M 7000,6800 L 5700,6800 5700,5800 8300,5800 8300,6800 7000,6800 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7000,6800 L 5700,6800 5700,5800 8300,5800 8300,6800 7000,6800 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6187" y="6421"><tspan fill="rgb(0,0,0)" stroke="none">Process C</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id8">
<rect class="BoundingBox" stroke="none" fill="none" x="9199" y="2499" width="3003" height="1003"/>
<path fill="rgb(255,215,215)" stroke="none" d="M 10700,3500 L 9200,3500 9200,2500 12200,2500 12200,3500 10700,3500 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10700,3500 L 9200,3500 9200,2500 12200,2500 12200,3500 10700,3500 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9490" y="3121"><tspan fill="rgb(0,0,0)" stroke="none">ProcessNode A</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id9">
<rect class="BoundingBox" stroke="none" fill="none" x="11699" y="4199" width="3003" height="1003"/>
<path fill="rgb(255,215,215)" stroke="none" d="M 13200,5200 L 11700,5200 11700,4200 14700,4200 14700,5200 13200,5200 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13200,5200 L 11700,5200 11700,4200 14700,4200 14700,5200 13200,5200 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11979" y="4821"><tspan fill="rgb(0,0,0)" stroke="none">ProcessNode B</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id10">
<rect class="BoundingBox" stroke="none" fill="none" x="10299" y="6799" width="3003" height="1003"/>
<path fill="rgb(255,215,215)" stroke="none" d="M 11800,7800 L 10300,7800 10300,6800 13300,6800 13300,7800 11800,7800 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11800,7800 L 10300,7800 10300,6800 13300,6800 13300,7800 11800,7800 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10568" y="7421"><tspan fill="rgb(0,0,0)" stroke="none">ProcessNode C</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id11">
<rect class="BoundingBox" stroke="none" fill="none" x="10373" y="3500" width="829" height="3302"/>
<path fill="none" stroke="rgb(18,118,34)" d="M 11200,6800 L 10468,3779"/>
<path fill="rgb(18,118,34)" stroke="none" d="M 10400,3500 L 10373,3815 10568,3768 10400,3500 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id12">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="3200" width="902" height="802"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8300,4000 L 8338,3966"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8376,3932 L 8414,3898"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8452,3864 L 8491,3831"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8529,3797 L 8567,3763"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8605,3729 L 8643,3695"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8681,3661 L 8719,3627"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8757,3593 L 8796,3560"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8834,3526 L 8872,3492"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8910,3458 L 8911,3457"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 9200,3200 L 9117,3408 8901,3466 8984,3258 9200,3200 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id13">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="4624" width="3402" height="478"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8300,5100 L 8351,5094"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8401,5088 L 8452,5082"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8503,5076 L 8553,5070"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8604,5064 L 8655,5058"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8705,5052 L 8756,5046"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8807,5040 L 8857,5034"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8908,5028 L 8958,5023"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9009,5017 L 9060,5011"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9110,5005 L 9161,4999"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9212,4993 L 9262,4987"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9313,4981 L 9364,4975"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9414,4969 L 9465,4963"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9516,4957 L 9566,4951"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9617,4945 L 9668,4939"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9718,4933 L 9769,4927"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9820,4921 L 9870,4915"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9921,4909 L 9971,4903"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10022,4897 L 10073,4891"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10123,4885 L 10174,4880"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10225,4874 L 10275,4868"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10326,4862 L 10377,4856"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10427,4850 L 10478,4844"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10529,4838 L 10579,4832"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10630,4826 L 10681,4820"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10731,4814 L 10782,4808"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10833,4802 L 10883,4796"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10934,4790 L 10984,4784"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11035,4778 L 11086,4772"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11136,4766 L 11187,4760"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11238,4754 L 11288,4748"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 11700,4700 L 11513,4823 11303,4747 11490,4624 11700,4700 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id14">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="6299" width="1902" height="907"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8300,6300 L 8346,6322"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8392,6344 L 8438,6365"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8484,6387 L 8530,6409"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8577,6431 L 8623,6453"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8669,6475 L 8715,6496"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8761,6518 L 8807,6540"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8853,6562 L 8899,6584"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8945,6606 L 8991,6627"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9037,6649 L 9084,6671"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9130,6693 L 9176,6715"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9222,6737 L 9268,6758"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9314,6780 L 9360,6802"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9406,6824 L 9452,6846"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9498,6868 L 9544,6889"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9591,6911 L 9637,6933"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9683,6955 L 9729,6977"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9775,6999 L 9821,7020"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 10200,7200 L 9976,7205 9839,7029 10062,7024 10200,7200 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id15">
<rect class="BoundingBox" stroke="none" fill="none" x="1499" y="5699" width="2603" height="1003"/>
<path fill="rgb(221,221,221)" stroke="none" d="M 2800,6700 L 1500,6700 1500,5700 4100,5700 4100,6700 2800,6700 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 2800,6700 L 1500,6700 1500,5700 4100,5700 4100,6700 2800,6700 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="2324" y="6321"><tspan fill="rgb(0,0,0)" stroke="none">Model</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id16">
<rect class="BoundingBox" stroke="none" fill="none" x="4200" y="3899" width="1502" height="1702"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 5700,3900 L 4390,5385"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 4200,5600 L 4473,5441 4324,5309 4200,5600 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id17">
<rect class="BoundingBox" stroke="none" fill="none" x="4200" y="5099" width="1502" height="902"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 5700,5100 L 4446,5853"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 4200,6000 L 4509,5931 4406,5760 4200,6000 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id18">
<rect class="BoundingBox" stroke="none" fill="none" x="4100" y="6200" width="1602" height="201"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 5700,6300 L 4387,6300"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 4100,6300 L 4400,6400 4400,6200 4100,6300 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id19">
<rect class="BoundingBox" stroke="none" fill="none" x="2299" y="3299" width="1204" height="1304"/>
<path fill="rgb(221,221,221)" stroke="none" d="M 2300,3504 C 2300,3402 2600,3300 2900,3300 3200,3300 3501,3402 3501,3504 L 3501,4396 C 3501,4498 3200,4601 2900,4601 2600,4601 2300,4498 2300,4396 L 2300,3504 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 2300,3504 C 2300,3402 2600,3300 2900,3300 3200,3300 3501,3402 3501,3504 L 3501,4396 C 3501,4498 3200,4601 2900,4601 2600,4601 2300,4498 2300,4396 L 2300,3504 Z"/>
<path fill="rgb(221,221,221)" stroke="none" d="M 2300,3504 C 2300,3606 2600,3709 2900,3709 3200,3709 3501,3606 3501,3504 L 2300,3504 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 2300,3504 C 2300,3606 2600,3709 2900,3709 3200,3709 3501,3606 3501,3504"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id20">
<rect class="BoundingBox" stroke="none" fill="none" x="3500" y="3627" width="2203" height="275"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 5701,3900 L 3785,3726"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 3500,3700 L 3790,3827 3808,3628 3500,3700 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id21">
<rect class="BoundingBox" stroke="none" fill="none" x="3500" y="3999" width="2203" height="1103"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 5701,5100 L 3756,4128"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 3500,4000 L 3724,4224 3813,4045 3500,4000 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id22">
<rect class="BoundingBox" stroke="none" fill="none" x="3500" y="4400" width="2203" height="1902"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 5701,6300 L 3717,4587"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 3500,4400 L 3662,4672 3792,4520 3500,4400 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id23">
<rect class="BoundingBox" stroke="none" fill="none" x="16299" y="3799" width="3003" height="1903"/>
<path fill="rgb(232,242,161)" stroke="none" d="M 17800,5700 L 16300,5700 16300,3800 19300,3800 19300,5700 17800,5700 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 17800,5700 L 16300,5700 16300,3800 19300,3800 19300,5700 17800,5700 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="16884" y="4871"><tspan fill="rgb(0,0,0)" stroke="none">Coordinator</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.PolyLineShape">
<g id="id24">
<rect class="BoundingBox" stroke="none" fill="none" x="12199" y="2999" width="4102" height="1402"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12200,3000 L 12250,3011"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12299,3023 L 12349,3034"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12399,3046 L 12448,3057"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12498,3069 L 12548,3080"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12598,3092 L 12647,3103"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12697,3115 L 12747,3126"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12796,3138 L 12846,3149"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12896,3161 L 12945,3172"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12995,3183 L 13045,3195"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13094,3206 L 13144,3218"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13194,3229 L 13244,3241"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13293,3252 L 13343,3264"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13393,3275 L 13442,3287"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13492,3298 L 13542,3310"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13591,3321 L 13641,3333"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13691,3344 L 13741,3356"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13790,3367 L 13840,3378"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13890,3390 L 13939,3401"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13989,3413 L 14039,3424"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14088,3436 L 14138,3447"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14188,3459 L 14237,3470"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14287,3482 L 14337,3493"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14387,3505 L 14436,3516"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14486,3528 L 14536,3539"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14585,3550 L 14635,3562"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14685,3573 L 14734,3585"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14784,3596 L 14800,3600 14831,3616"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14876,3640 L 14921,3664"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14966,3688 L 15011,3712"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15056,3736 L 15101,3760"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15146,3784 L 15191,3808"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15236,3832 L 15281,3856"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15326,3880 L 15371,3904"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15416,3928 L 15461,3952"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15506,3976 L 15551,4000"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15596,4024 L 15641,4048"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15686,4072 L 15731,4096"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15776,4120 L 15821,4144"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15866,4168 L 15911,4192"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15956,4216 L 15959,4218"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 16300,4400 L 16076,4394 15947,4212 16171,4218 16300,4400 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id25">
<rect class="BoundingBox" stroke="none" fill="none" x="14699" y="4787" width="1602" height="202"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14700,4800 L 14751,4803"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14802,4806 L 14853,4810"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14904,4813 L 14955,4816"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15005,4819 L 15056,4822"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15107,4825 L 15158,4829"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15209,4832 L 15260,4835"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15311,4838 L 15362,4841"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15413,4845 L 15464,4848"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15514,4851 L 15565,4854"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15616,4857 L 15667,4860"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15718,4864 L 15769,4867"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15820,4870 L 15871,4873"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 16300,4900 L 16094,4987 15901,4875 16107,4788 16300,4900 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id26">
<rect class="BoundingBox" stroke="none" fill="none" x="13299" y="5500" width="3002" height="1702"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13300,7200 L 13344,7175"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13389,7150 L 13433,7125"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13477,7099 L 13522,7074"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13566,7049 L 13611,7024"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13655,6999 L 13699,6974"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13744,6949 L 13788,6923"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13832,6898 L 13877,6873"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13921,6848 L 13966,6823"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14010,6798 L 14054,6773"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14099,6747 L 14143,6722"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14187,6697 L 14232,6672"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14276,6647 L 14321,6622"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14365,6597 L 14409,6571"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14454,6546 L 14498,6521"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14542,6496 L 14587,6471"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14631,6446 L 14676,6421"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14720,6395 L 14764,6370"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14809,6345 L 14853,6320"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14897,6295 L 14942,6270"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 14986,6245 L 15030,6219"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15075,6194 L 15119,6169"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15164,6144 L 15208,6119"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15252,6094 L 15297,6069"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15341,6043 L 15385,6018"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15430,5993 L 15474,5968"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15519,5943 L 15563,5918"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15607,5893 L 15652,5867"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15696,5842 L 15740,5817"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15785,5792 L 15829,5767"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15874,5742 L 15918,5717"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15962,5691 L 15964,5691"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 16300,5500 L 16175,5686 15952,5697 16077,5512 16300,5500 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id27">
<rect class="BoundingBox" stroke="none" fill="none" x="13699" y="9399" width="3003" height="1003"/>
<path fill="rgb(221,221,221)" stroke="none" d="M 15200,10400 L 13700,10400 13700,9400 16700,9400 16700,10400 15200,10400 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15200,10400 L 13700,10400 13700,9400 16700,9400 16700,10400 15200,10400 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="14199" y="10021"><tspan fill="rgb(0,0,0)" stroke="none">MainWindow</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id28">
<rect class="BoundingBox" stroke="none" fill="none" x="15761" y="5699" width="1141" height="3702"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15910,9029 L 15925,8980"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15939,8932 L 15954,8883"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15968,8834 L 15983,8785"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 15997,8736 L 16012,8687"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16026,8638 L 16041,8589"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16056,8541 L 16070,8492"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16085,8443 L 16099,8394"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16114,8345 L 16128,8296"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16143,8247 L 16157,8198"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16172,8149 L 16186,8101"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16201,8052 L 16215,8003"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16230,7954 L 16244,7905"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16259,7856 L 16274,7807"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16288,7758 L 16303,7709"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16317,7661 L 16332,7612"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16346,7563 L 16361,7514"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16375,7465 L 16390,7416"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16404,7367 L 16419,7318"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16433,7269 L 16448,7221"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16462,7172 L 16477,7123"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16492,7074 L 16506,7025"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16521,6976 L 16535,6927"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16550,6878 L 16564,6830"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16579,6781 L 16593,6732"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16608,6683 L 16622,6634"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16637,6585 L 16651,6536"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16666,6487 L 16680,6438"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16695,6390 L 16710,6341"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16724,6292 L 16739,6243"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16753,6194 L 16768,6145"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16782,6096 L 16797,6047"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16811,5998 L 16826,5950"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16840,5901 L 16855,5852"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16869,5803 L 16884,5754"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 16898,5705 L 16900,5700"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 15800,9400 L 15761,9180 15914,9017 15953,9237 15800,9400 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.PolyLineShape">
<g id="id29">
<rect class="BoundingBox" stroke="none" fill="none" x="2799" y="6699" width="10902" height="3400"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13313,9995 L 13262,9994"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13211,9993 L 13160,9992"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13109,9992 L 13058,9991"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 13007,9990 L 12956,9990"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12905,9989 L 12854,9988"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12803,9987 L 12752,9987"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12701,9986 L 12650,9985"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12599,9984 L 12548,9984"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12497,9983 L 12446,9982"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12395,9982 L 12344,9981"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12293,9980 L 12242,9979"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12191,9979 L 12140,9978"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12089,9977 L 12038,9977"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11988,9976 L 11937,9975"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11886,9974 L 11835,9974"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11784,9973 L 11733,9972"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11682,9972 L 11631,9971"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11580,9970 L 11529,9969"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11478,9969 L 11427,9968"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11376,9967 L 11325,9967"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11274,9966 L 11223,9965"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11172,9964 L 11121,9964"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11070,9963 L 11019,9962"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10968,9962 L 10917,9961"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10866,9960 L 10815,9959"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10764,9959 L 10713,9958"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10662,9957 L 10611,9956"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10560,9956 L 10509,9955"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10458,9954 L 10407,9954"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10356,9953 L 10305,9952"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10254,9951 L 10203,9951"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10152,9950 L 10101,9949"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 10050,9949 L 9999,9948"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9948,9947 L 9897,9946"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9846,9946 L 9795,9945"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9744,9944 L 9693,9944"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9642,9943 L 9591,9942"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9540,9941 L 9489,9941"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9438,9940 L 9387,9939"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9336,9939 L 9285,9938"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9234,9937 L 9183,9936"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9132,9936 L 9081,9935"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 9030,9934 L 8979,9934"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8928,9933 L 8877,9932"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8826,9931 L 8775,9931"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8724,9930 L 8673,9929"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8622,9928 L 8571,9928"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8520,9927 L 8469,9926"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8418,9926 L 8367,9925"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8316,9924 L 8265,9923"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8214,9923 L 8163,9922"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8112,9921 L 8061,9921"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8010,9920 L 7959,9919"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7908,9918 L 7857,9918"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7806,9917 L 7755,9916"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7704,9916 L 7653,9915"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7602,9914 L 7551,9913"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7500,9913 L 7449,9912"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7398,9911 L 7347,9911"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7296,9910 L 7245,9909"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7194,9908 L 7143,9908"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 7092,9907 L 7041,9906"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6990,9905 L 6939,9905"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6888,9904 L 6837,9903"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6786,9903 L 6735,9902"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6684,9901 L 6633,9900"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6586,9888 L 6547,9856"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6508,9823 L 6469,9790"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6430,9757 L 6391,9724"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6352,9691 L 6313,9658"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6274,9626 L 6235,9593"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6196,9560 L 6157,9527"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6118,9494 L 6079,9461"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6040,9429 L 6001,9396"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5962,9363 L 5923,9330"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5884,9297 L 5845,9264"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5806,9231 L 5767,9199"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5728,9166 L 5689,9133"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5650,9100 L 5611,9067"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5572,9034 L 5533,9001"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5494,8969 L 5455,8936"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5416,8903 L 5377,8870"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5338,8837 L 5299,8804"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5260,8771 L 5221,8739"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5182,8706 L 5143,8673"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5104,8640 L 5065,8607"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 5026,8574 L 4987,8542"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4948,8509 L 4909,8476"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4870,8443 L 4831,8410"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4792,8377 L 4753,8344"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4714,8312 L 4675,8279"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4636,8246 L 4597,8213"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4558,8180 L 4519,8147"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4480,8114 L 4441,8082"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4402,8049 L 4363,8016"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4324,7983 L 4285,7950"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4246,7917 L 4207,7885"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4168,7852 L 4129,7819"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4090,7786 L 4051,7753"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 4012,7720 L 3973,7687"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3934,7655 L 3895,7622"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3856,7589 L 3817,7556"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3778,7523 L 3738,7490"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3699,7457 L 3660,7425"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3621,7392 L 3582,7359"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3543,7326 L 3504,7293"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3465,7260 L 3426,7228"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3387,7195 L 3348,7162"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3309,7129 L 3270,7096"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3231,7063 L 3192,7030"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3153,6998 L 3114,6965"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 3075,6932 L 3036,6899"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 2997,6866 L 2958,6833"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 2919,6800 L 2880,6768"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 2841,6735 L 2802,6702"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 13700,10000 L 13499,10097 13300,9994 13501,9897 13700,10000 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.PolyLineShape">
<g id="id30">
<rect class="BoundingBox" stroke="none" fill="none" x="3399" y="6699" width="10302" height="3094"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 13413,9692 L 6800,9500 3400,6700"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 13700,9700 L 13403,9591 13397,9791 13700,9700 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id31">
<rect class="BoundingBox" stroke="none" fill="none" x="16189" y="5699" width="1113" height="3702"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 17300,5700 L 16282,9125"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 16200,9400 L 16381,9141 16190,9084 16200,9400 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id32">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="5230" width="8002" height="872"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 8300,6100 L 16015,5329"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 16300,5300 L 15992,5230 16011,5429 16300,5300 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.PolyLineShape">
<g id="id33">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="3799" width="8002" height="1003"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 8300,4800 L 11100,4000 14300,3800 16039,4582"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 16300,4700 L 16067,4486 15985,4668 16300,4700 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.PolyLineShape">
<g id="id34">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="1999" width="8002" height="2102"/>
<path fill="none" stroke="rgb(241,13,12)" d="M 8300,3500 L 9000,2000 14600,2100 16114,3882"/>
<path fill="rgb(241,13,12)" stroke="none" d="M 16300,4100 L 16182,3807 16030,3936 16300,4100 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.TextShape">
<g id="id35">
<rect class="BoundingBox" stroke="none" fill="none" x="1877" y="2100" width="2124" height="1040"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="2127" y="2543"><tspan fill="rgb(0,0,0)" stroke="none">Local Disk</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="2351" y="2937"><tspan fill="rgb(0,0,0)" stroke="none">System</tspan></tspan></tspan></text>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 52 KiB

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="113.01mm" height="96.01mm" viewBox="3800 1500 11301 9601" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
<defs class="ClipPathGroup">
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
<rect x="3800" y="1500" width="11301" height="9601"/>
</clipPath>
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
<rect x="3811" y="1509" width="11279" height="9582"/>
</clipPath>
</defs>
<defs>
<font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="y" horiz-adv-x="1033" d="M 191,-425 C 142,-425 100,-421 67,-414 L 67,-279 C 92,-283 120,-285 151,-285 263,-285 352,-203 417,-38 L 434,5 5,1082 197,1082 425,484 C 428,475 432,464 437,451 442,438 457,394 482,320 507,246 521,205 523,196 L 593,393 830,1082 1020,1082 604,0 C 559,-115 518,-201 479,-258 440,-314 398,-356 351,-384 304,-411 250,-425 191,-425 Z"/>
<glyph unicode="x" horiz-adv-x="1006" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
<glyph unicode="v" horiz-adv-x="1033" d="M 613,0 L 400,0 7,1082 199,1082 437,378 C 446,351 469,272 506,141 L 541,258 580,376 826,1082 1017,1082 613,0 Z"/>
<glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/>
<glyph unicode="s" horiz-adv-x="901" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
<glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
<glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 488,-20 376,43 319,168 L 314,168 C 317,163 318,106 318,-2 L 318,-425 138,-425 138,861 C 138,972 136,1046 132,1082 L 306,1082 C 307,1079 308,1070 309,1054 310,1037 312,1012 314,978 315,944 316,921 316,908 L 320,908 C 352,975 394,1024 447,1055 500,1086 569,1101 655,1101 788,1101 888,1056 954,967 1020,878 1053,737 1053,546 Z M 864,542 C 864,693 844,800 803,865 762,930 698,962 609,962 538,962 482,947 442,917 401,887 371,840 350,777 329,713 318,630 318,528 318,386 341,281 386,214 431,147 505,113 607,113 696,113 762,146 803,212 844,277 864,387 864,542 Z"/>
<glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/>
<glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/>
<glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
<glyph unicode="k" horiz-adv-x="901" d="M 816,0 L 450,494 318,385 318,0 138,0 138,1484 318,1484 318,557 793,1082 1004,1082 565,617 1027,0 816,0 Z"/>
<glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
<glyph unicode="g" horiz-adv-x="927" d="M 548,-425 C 430,-425 336,-402 266,-356 196,-309 151,-243 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 732,-288 822,-183 822,27 L 822,201 820,201 C 786,132 739,80 680,45 621,10 551,-8 472,-8 339,-8 242,36 180,124 117,212 86,350 86,539 86,730 120,872 187,963 254,1054 355,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,917 825,952 828,1001 831,1050 833,1077 836,1082 L 1007,1082 C 1003,1046 1001,971 1001,858 L 1001,31 C 1001,-273 850,-425 548,-425 Z M 822,541 C 822,629 810,705 786,769 762,832 728,881 685,915 641,948 591,965 536,965 444,965 377,932 335,865 293,798 272,690 272,541 272,393 292,287 331,222 370,157 438,125 533,125 590,125 640,142 684,175 728,208 762,256 786,319 810,381 822,455 822,541 Z"/>
<glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
<glyph unicode="d" horiz-adv-x="927" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 821,1035 821,1484 1001,1484 1001,223 C 1001,110 1003,36 1007,0 L 835,0 C 833,11 831,35 829,74 826,113 825,146 825,174 L 821,174 Z M 275,542 C 275,391 295,282 335,217 375,152 440,119 530,119 632,119 706,154 752,225 798,296 821,405 821,554 821,697 798,802 752,869 706,936 633,969 532,969 441,969 376,936 336,869 295,802 275,693 275,542 Z"/>
<glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/>
<glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
<glyph unicode="U" horiz-adv-x="1192" d="M 731,-20 C 616,-20 515,1 429,43 343,85 276,146 229,226 182,306 158,401 158,512 L 158,1409 349,1409 349,528 C 349,399 382,302 447,235 512,168 607,135 730,135 857,135 955,170 1026,239 1096,308 1131,408 1131,541 L 1131,1409 1321,1409 1321,530 C 1321,416 1297,318 1249,235 1200,152 1132,89 1044,46 955,2 851,-20 731,-20 Z"/>
<glyph unicode="S" horiz-adv-x="1192" d="M 1272,389 C 1272,259 1221,158 1120,87 1018,16 875,-20 690,-20 347,-20 148,99 93,338 L 278,375 C 299,290 345,228 414,189 483,149 578,129 697,129 820,129 916,150 983,193 1050,235 1083,297 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 541,675 456,699 399,724 341,749 295,776 262,807 229,837 203,872 186,913 168,954 159,1000 159,1053 159,1174 205,1267 298,1332 390,1397 522,1430 694,1430 854,1430 976,1406 1061,1357 1146,1308 1205,1224 1239,1106 L 1051,1073 C 1030,1148 991,1202 933,1236 875,1269 795,1286 692,1286 579,1286 493,1267 434,1230 375,1193 345,1137 345,1063 345,1020 357,984 380,956 403,927 436,903 479,884 522,864 609,840 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z"/>
<glyph unicode="R" horiz-adv-x="1244" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 990,1409 1112,1374 1199,1303 1285,1232 1328,1133 1328,1006 1328,901 1298,813 1237,742 1176,671 1091,626 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1086 1108,1149 1053,1192 997,1235 917,1256 812,1256 L 359,1256 359,736 820,736 C 921,736 999,760 1054,807 1109,854 1136,919 1136,1004 Z"/>
<glyph unicode="P" horiz-adv-x="1112" d="M 1258,985 C 1258,852 1215,746 1128,667 1041,588 922,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 919,1409 1041,1372 1128,1298 1215,1224 1258,1120 1258,985 Z M 1066,983 C 1066,1165 957,1256 738,1256 L 359,1256 359,700 746,700 C 959,700 1066,794 1066,983 Z"/>
<glyph unicode="L" horiz-adv-x="927" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z"/>
<glyph unicode="I" horiz-adv-x="213" d="M 189,0 L 189,1409 380,1409 380,0 189,0 Z"/>
<glyph unicode="E" horiz-adv-x="1138" d="M 168,0 L 168,1409 1237,1409 1237,1253 359,1253 359,801 1177,801 1177,647 359,647 359,156 1278,156 1278,0 168,0 Z"/>
<glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,574 1353,447 1296,338 1239,229 1159,145 1055,87 951,29 831,0 695,0 L 168,0 168,1409 634,1409 C 873,1409 1057,1349 1187,1230 1316,1110 1381,940 1381,719 Z M 1189,719 C 1189,894 1141,1027 1046,1119 950,1210 811,1256 630,1256 L 359,1256 359,153 673,153 C 776,153 867,176 946,221 1024,266 1084,332 1126,417 1168,502 1189,603 1189,719 Z"/>
</font>
</defs>
<defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13"/>
</defs>
<defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
</g>
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
</g>
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
</g>
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
</g>
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
</g>
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
</g>
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
</g>
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
</g>
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
</g>
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
</g>
</defs>
<defs class="TextEmbeddedBitmaps"/>
<g class="SlideGroup">
<g>
<g id="container-id1">
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
<g class="Page">
<g class="com.sun.star.drawing.CustomShape">
<g id="id3">
<rect class="BoundingBox" stroke="none" fill="none" x="8599" y="3999" width="5003" height="1503"/>
<path fill="rgb(180,199,220)" stroke="none" d="M 11100,5500 L 8600,5500 8600,4000 13600,4000 13600,5500 11100,5500 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11100,5500 L 8600,5500 8600,4000 13600,4000 13600,5500 11100,5500 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9276" y="4674"><tspan fill="rgb(0,0,0)" stroke="none">LocalRepositoryUpdate</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10463" y="5068"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id4">
<rect class="BoundingBox" stroke="none" fill="none" x="8599" y="1499" width="5003" height="1503"/>
<path fill="rgb(180,199,220)" stroke="none" d="M 11100,3000 L 8600,3000 8600,1500 13600,1500 13600,3000 11100,3000 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11100,3000 L 8600,3000 8600,1500 13600,1500 13600,3000 11100,3000 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9619" y="2174"><tspan fill="rgb(0,0,0)" stroke="none">LocalPkgDataLoad</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10463" y="2568"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id5">
<rect class="BoundingBox" stroke="none" fill="none" x="9999" y="6699" width="5003" height="1503"/>
<path fill="rgb(255,219,182)" stroke="none" d="M 12500,8200 L 10000,8200 10000,6700 15000,6700 15000,8200 12500,8200 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12500,8200 L 10000,8200 10000,6700 15000,6700 15000,8200 12500,8200 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10205" y="7374"><tspan fill="rgb(0,0,0)" stroke="none">ServerRepositoryDataUpdate</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11863" y="7768"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id6">
<rect class="BoundingBox" stroke="none" fill="none" x="3799" y="6699" width="5003" height="1503"/>
<path fill="rgb(255,219,182)" stroke="none" d="M 6300,8200 L 3800,8200 3800,6700 8800,6700 8800,8200 6300,8200 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 6300,8200 L 3800,8200 3800,6700 8800,6700 8800,8200 6300,8200 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4376" y="7374"><tspan fill="rgb(0,0,0)" stroke="none">ServerIconExportUpdate</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="5663" y="7768"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id7">
<rect class="BoundingBox" stroke="none" fill="none" x="9699" y="9599" width="5003" height="1503"/>
<path fill="rgb(255,219,182)" stroke="none" d="M 12200,11100 L 9700,11100 9700,9600 14700,9600 14700,11100 12200,11100 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12200,11100 L 9700,11100 9700,9600 14700,9600 14700,11100 12200,11100 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10443" y="10274"><tspan fill="rgb(0,0,0)" stroke="none">ServerPkgDataUpdate</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11563" y="10668"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id8">
<rect class="BoundingBox" stroke="none" fill="none" x="9899" y="9399" width="5003" height="1503"/>
<path fill="rgb(255,219,182)" stroke="none" d="M 12400,10900 L 9900,10900 9900,9400 14900,9400 14900,10900 12400,10900 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12400,10900 L 9900,10900 9900,9400 14900,9400 14900,10900 12400,10900 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10643" y="10074"><tspan fill="rgb(0,0,0)" stroke="none">ServerPkgDataUpdate</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11763" y="10468"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id9">
<rect class="BoundingBox" stroke="none" fill="none" x="10099" y="9099" width="5003" height="1503"/>
<path fill="rgb(255,219,182)" stroke="none" d="M 12600,10600 L 10100,10600 10100,9100 15100,9100 15100,10600 12600,10600 Z"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12600,10600 L 10100,10600 10100,9100 15100,9100 15100,10600 12600,10600 Z"/>
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="10843" y="9774"><tspan fill="rgb(0,0,0)" stroke="none">ServerPkgDataUpdate</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="11963" y="10168"><tspan fill="rgb(0,0,0)" stroke="none">Process</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id10">
<rect class="BoundingBox" stroke="none" fill="none" x="11000" y="3100" width="201" height="902"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 11100,4000 L 11100,3387"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 11100,3100 L 11000,3400 11200,3400 11100,3100 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id11">
<rect class="BoundingBox" stroke="none" fill="none" x="8299" y="5500" width="902" height="1202"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 8300,6700 L 9028,5729"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 9200,5500 L 8940,5680 9100,5800 9200,5500 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id12">
<rect class="BoundingBox" stroke="none" fill="none" x="12075" y="5500" width="327" height="1202"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12400,6700 L 12170,5778"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 12100,5500 L 12076,5815 12270,5767 12100,5500 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.LineShape">
<g id="id13">
<rect class="BoundingBox" stroke="none" fill="none" x="12300" y="8200" width="201" height="902"/>
<path fill="none" stroke="rgb(0,0,0)" d="M 12400,9100 L 12400,8487"/>
<path fill="rgb(0,0,0)" stroke="none" d="M 12400,8200 L 12300,8500 12500,8500 12400,8200 Z"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

+60
View File
@@ -0,0 +1,60 @@
# HaikuDepot and Server Interactions
## Introduction
This document aims to outline the general approach taken within the HaikuDepot application with regard to coordinating processes that relate to fetching and consuming data from remote systems.
There are two main sources of remote data that are downloaded and consumed from network sources into the HaikuDepot desktop application;
* Repository HPKR data from a Haiku mirror such as "HaikuPorts"
* Meta-data related to packages from [HaikuDepotServer](http://depot.haiku-os.org) (HDS) such as icons, localizations, ratings and so on.
## Process, ProcessNode and Coordinator
A _Process_ (root class ```AbstractProcess```) is a class that takes responsibility for some aspect of pulling material down from a network source and processing it.
A _ProcessNode_ is a holder for a Process, but also takes responsibility for the following;
* Maintaining the relationship between the Processes. For example, if Process A needs to complete before Process B then the ProcessNode would record this fact. It does this by storing _predecessor_ and _successor_ ProcessNodes.
* Starting the held Process in a newly spawned thread.
* Stopping the held Process.
A _Coordinator_ holds a list of ProcessNodes. It will start, stop and cancel nodes as necessary such that, in an ideal case, the various ProcessNodes are completed in the correct order.
The _ProcessCoordinatorFactory_ is able to create Coordinators.
## Bulk Load Processes
The following diagram shows the logical dependencies of the various Processes that are involved in refreshing the HPKR data from remote repositories and then loading data from the HDS system.
![Process Dependencies](images/processes.svg)
For example, the ```ServerRepositoryDataUpdateProcess``` must wait until the ```LocalRepositoryUpdateProcess``` has completed before it is able to be started. It is the reponsibility of the Coordinator to ensure that this sequencing is enforced. There are many instances of ```ServerPkgDataUpdateProcess``` shown because there will be one launched for each of the Repositories for which data will be downloaded; "HaikuDepot" etc...
## Process / ProcessNode / Coordinator
The following diagram shows the relationship and interplay between the various objects that are involved in running a larger task. Only fictional Processes are shown to keep the diagram tidy. See above for the actual Processes.
![Process Relationship and Interplay](images/process-interplay.svg)
Dotted lines show associations between elements and red lines show interaction or data-flow. Green arrows here demonstratively show some dependency; Process C cannot start until A and B are completed.
The MainWindow owns the Coordinator for the life-span of undertaking some larger task.
Each Process is coupled with a ProcessNode and then the Coordinator has a list of the ProcessNodes-s. The Processes are generally writing to the local disk system (often with compressed files) to cache data (see ```~/config/cache/HaikuDepot```) and also relay data into the ```Model``` object that maintains state for the HaikuDepot desktop application.
The Processes communicate when they have finished to the Coordinator and it is at these events that the Coordinator is able to introspect the state of the Processes in order to know what to do next.
The Coordinator also communicates with MainWindow. It communicates with the MainWindow in order to signal changes or progress in the overall larger task. The MainWindow also uses these events to discover when the Coordinator has completely finished.
## Failure
A Process may fail or be stopped. If a Process fails or is stopped then successor Processes, or those that would have run after the failed process, are stopped so that they will not run.
The Coordinator will still try to complete any other Processes that could still run or are running already.
Upon the Coordinator completing, the Coordinator will signal to the MainWindow client the change in state and then the MainWindow will be able to identify that the Coordinator has completed, but that something has gone wrong along the way.
## Concurrency
It is important to note that Processes may run concurrently. The Processes' are modelled by the Coordinator as a list rather than a tree. The dependencies are likely to form a tree or web of Processes that dictates the order of execution, but it is also quite possible to have multiple non-intersecting trees or webs such that Processes will execute independently.
@@ -16,6 +16,7 @@ enum {
MSG_NETWORK_TRANSPORT_ERROR = 'nett',
MSG_SERVER_ERROR = 'svre',
MSG_SERVER_DATA_CHANGED = 'svdc',
MSG_ALERT_SIMPLE_ERROR = 'nser',
MSG_DID_ADD_USER_RATING = 'adur',
MSG_DID_UPDATE_USER_RATING = 'upur'
};
@@ -36,6 +37,14 @@ enum {
#define REPOSITORY_NAME_INSTALLED "installed"
#define KEY_ALERT_TEXT "alert_text"
#define KEY_ALERT_TITLE "alert_title"
#define KEY_WORK_STATUS_TEXT "work_status_text"
#define KEY_WORK_STATUS_PROGRESS "work_status_progress"
#define KEY_WINDOW_SETTINGS "window_settings"
#define KEY_MAIN_SETTINGS "main_settings"
// These constants reference resources in 'HaikuDepot.ref'
enum {
RSRC_STAR_BLUE = 510,
+11 -6
View File
@@ -69,8 +69,6 @@ Application HaikuDepot :
MarkupTextView.cpp
MessagePackageListener.cpp
Model.cpp
BulkLoadContext.cpp
BulkLoadStateMachine.cpp
PackageAction.cpp
PackageActionHandler.cpp
PackageContentsView.cpp
@@ -98,23 +96,30 @@ Application HaikuDepot :
DumpExportRepositorySource.cpp
DumpExportRepositoryJsonListener.cpp
# network + server
# network + server / local processes
AbstractProcess.cpp
AbstractServerProcess.cpp
AbstractSingleFileServerProcess.cpp
LocalPkgDataLoadProcess.cpp
LocalRepositoryUpdateProcess.cpp
ProcessCoordinator.cpp
ProcessCoordinatorFactory.cpp
ProcessNode.cpp
ServerHelper.cpp
ServerSettings.cpp
WebAppInterface.cpp
PkgDataUpdateProcess.cpp
RepositoryDataUpdateProcess.cpp
ServerPkgDataUpdateProcess.cpp
ServerRepositoryDataUpdateProcess.cpp
ServerIconExportUpdateProcess.cpp
StandardMetaDataJsonEventListener.cpp
StandardMetaData.cpp
WebAppInterface.cpp
# tar
TarArchiveHeader.cpp
TarArchiveService.cpp
#util
AppUtils.cpp
DataIOUtils.cpp
RepositoryUrlUtils.cpp
StorageUtils.cpp
+138 -88
View File
@@ -1,5 +1,6 @@
/*
* Copyright 2009-2013, Stephan Aßmus <[email protected]>
* Copyright 2018, Andrew Lindesay <[email protected]>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LIST_H
@@ -20,12 +21,28 @@
template <typename ItemType, bool PlainOldData, uint32 BlockSize = 8>
class List {
typedef List<ItemType, PlainOldData, BlockSize> SelfType;
typedef int32 (*CompareItemFn)(const ItemType& one, const ItemType& two);
typedef int32 (*CompareContextFn)(const void* context,
const ItemType& item);
public:
List()
:
fItems(NULL),
fCount(0),
fAllocatedCount(0)
fAllocatedCount(0),
fCompareItemsFunction(NULL),
fCompareContextFunction(NULL)
{
}
List(CompareItemFn compareItemsFunction,
CompareContextFn compareContextFunction)
:
fItems(NULL),
fCount(0),
fAllocatedCount(0),
fCompareItemsFunction(compareItemsFunction),
fCompareContextFunction(compareContextFunction)
{
}
@@ -33,9 +50,11 @@ public:
:
fItems(NULL),
fCount(0),
fAllocatedCount(0)
fAllocatedCount(0),
fCompareItemsFunction(other.fCompareItemsFunction),
fCompareContextFunction(other.fCompareContextFunction)
{
*this = other;
_AddAllVerbatim(other);
}
virtual ~List()
@@ -49,28 +68,11 @@ public:
SelfType& operator=(const SelfType& other)
{
if (this == &other)
if (this != &other)
_AddAllVerbatim(other);
return *this;
}
if (PlainOldData) {
if (_Resize(other.fCount))
memcpy(fItems, other.fItems, fCount * sizeof(ItemType));
} else {
// Make sure to call destructors of old objects.
// NOTE: Another option would be to use
// ItemType::operator=(const ItemType& other), but then
// we would need to be carefull which objects are already
// initialized. Also ItemType would be required to implement the
// operator, while doing it this way requires only a copy
// constructor.
_Resize(0);
for (uint32 i = 0; i < other.fCount; i++) {
if (!Add(other.ItemAtFast(i)))
break;
}
}
return *this;
}
bool operator==(const SelfType& other) const
{
@@ -114,64 +116,40 @@ public:
return fCount;
}
/*! Note that the use of this method will depend on the list being ordered.
/*! Note that the use of this method will depend on the list being sorted.
*/
inline int32 BinarySearch(const void* context,
int32 (*compareFunc)(const void* context, const ItemType& item))
inline int32 Search(const void* context) const
{
if (fCount == 0)
if (fCount == 0 || fCompareContextFunction == NULL)
return -1;
return _BinarySearchBounded(context, compareFunc, 0, fCount - 1);
return _BinarySearchBounded(context, 0, fCount - 1);
}
inline bool AddOrdered(const ItemType& copyFrom,
int32 (*compareFunc)(const ItemType& one, const ItemType& two))
{
// special case
if (fCount == 0
|| compareFunc(copyFrom, ItemAtFast(fCount - 1)) > 0) {
return Add(copyFrom);
}
return _AddOrderedBounded(copyFrom, compareFunc, 0, fCount - 1);
}
/*! This function will add the item into the list. If the list is sorted
then the item will be insert in order. If the list is not sorted then
the item will be inserted at the end of the list.
*/
inline bool Add(const ItemType& copyFrom)
{
if (_Resize(fCount + 1)) {
ItemType* item = fItems + fCount - 1;
// Initialize the new object from the original.
if (!PlainOldData)
new (item) ItemType(copyFrom);
else
*item = copyFrom;
return true;
if (fCompareItemsFunction != NULL) {
return _AddOrdered(copyFrom);
}
return false;
return _AddTail(copyFrom);
}
inline bool Add(const ItemType& copyFrom, int32 index)
{
if (index < 0 || index > (int32)fCount)
return false;
// if the list is sorted then ignore the index and just insert in
// order.
if (fCompareItemsFunction != NULL) {
return _AddOrdered(copyFrom);
}
if (!_Resize(fCount + 1))
return false;
int32 nextIndex = index + 1;
if ((int32)fCount > nextIndex)
memmove(fItems + nextIndex, fItems + index,
(fCount - nextIndex) * sizeof(ItemType));
ItemType* item = fItems + index;
if (!PlainOldData)
new (item) ItemType(copyFrom);
else
*item = copyFrom;
return true;
return _AddAtIndex(copyFrom, index);
}
inline bool Remove()
@@ -210,6 +188,12 @@ public:
inline bool Replace(int32 index, const ItemType& copyFrom)
{
if (fCompareItemsFunction != NULL) {
bool result = Remove(index);
_AddOrdered(copyFrom);
return result;
}
if (index < 0 || index >= (int32)fCount)
return false;
@@ -259,11 +243,10 @@ public:
private:
inline int32 _BinarySearchLinearBounded(
const void* context,
int32 (*compareFunc)(const void* context, const ItemType& item),
int32 start, int32 end)
int32 start, int32 end) const
{
for(int32 i = start; i <= end; i++) {
if (compareFunc(context, ItemAtFast(i)) == 0)
if (fCompareContextFunction(context, ItemAtFast(i)) == 0)
return i;
}
@@ -271,35 +254,53 @@ private:
}
inline int32 _BinarySearchBounded(
const void* context,
int32 (*compareFunc)(const void* context, const ItemType& item),
int32 start, int32 end)
const void* context, int32 start, int32 end) const
{
if (end - start < BINARY_SEARCH_LINEAR_THRESHOLD)
return _BinarySearchLinearBounded(context, compareFunc, start, end);
return _BinarySearchLinearBounded(context, start, end);
int32 mid = start + ((end - start) >> 1);
if (compareFunc(context, ItemAtFast(mid)) >= 0)
return _BinarySearchBounded(context, compareFunc, mid, end);
return _BinarySearchBounded(context, compareFunc, start, mid - 1);
if (fCompareContextFunction(context, ItemAtFast(mid)) >= 0)
return _BinarySearchBounded(context, mid, end);
return _BinarySearchBounded(context, start, mid - 1);
}
inline void _AddAllVerbatim(const SelfType& other)
{
if (PlainOldData) {
if (_Resize(other.fCount))
memcpy(fItems, other.fItems, fCount * sizeof(ItemType));
} else {
// Make sure to call destructors of old objects.
// NOTE: Another option would be to use
// ItemType::operator=(const ItemType& other), but then
// we would need to be careful which objects are already
// initialized. Also ItemType would be required to implement the
// operator, while doing it this way requires only a copy
// constructor.
_Resize(0);
for (uint32 i = 0; i < other.fCount; i++) {
if (!Add(other.ItemAtFast(i)))
break;
}
}
}
inline bool _AddOrderedLinearBounded(
const ItemType& copyFrom,
int32 (*compareFunc)(const ItemType& one, const ItemType& two),
int32 start, int32 end)
const ItemType& copyFrom, int32 start, int32 end)
{
for(int32 i = start; i <= (end + 1); i++) {
bool greaterBefore = (i == start)
|| (compareFunc(copyFrom, ItemAtFast(i - 1)) > 0);
|| (fCompareItemsFunction(copyFrom, ItemAtFast(i - 1)) > 0);
if (greaterBefore) {
bool lessAfter = (i == end + 1)
|| (compareFunc(copyFrom, ItemAtFast(i)) <= 0);
|| (fCompareItemsFunction(copyFrom, ItemAtFast(i)) <= 0);
if (lessAfter)
return Add(copyFrom, i);
return _AddAtIndex(copyFrom, i);
}
}
@@ -308,18 +309,65 @@ private:
}
inline bool _AddOrderedBounded(
const ItemType& copyFrom,
int32 (*compareFunc)(const ItemType& one, const ItemType& two),
int32 start, int32 end)
const ItemType& copyFrom, int32 start, int32 end)
{
if(end - start < BINARY_SEARCH_LINEAR_THRESHOLD)
return _AddOrderedLinearBounded(copyFrom, compareFunc, start, end);
return _AddOrderedLinearBounded(copyFrom, start, end);
int32 mid = start + ((end - start) >> 1);
if (compareFunc(copyFrom, ItemAtFast(mid)) >= 0)
return _AddOrderedBounded(copyFrom, compareFunc, mid, end);
return _AddOrderedBounded(copyFrom, compareFunc, start, mid - 1);
if (fCompareItemsFunction(copyFrom, ItemAtFast(mid)) >= 0)
return _AddOrderedBounded(copyFrom, mid, end);
return _AddOrderedBounded(copyFrom, start, mid - 1);
}
inline bool _AddTail(const ItemType& copyFrom)
{
if (_Resize(fCount + 1)) {
ItemType* item = fItems + fCount - 1;
// Initialize the new object from the original.
if (!PlainOldData)
new (item) ItemType(copyFrom);
else
*item = copyFrom;
return true;
}
return false;
}
inline bool _AddAtIndex(const ItemType& copyFrom, int32 index)
{
if (index < 0 || index > (int32)fCount)
return false;
if (!_Resize(fCount + 1))
return false;
int32 nextIndex = index + 1;
if ((int32)fCount > nextIndex)
memmove(fItems + nextIndex, fItems + index,
(fCount - nextIndex) * sizeof(ItemType));
ItemType* item = fItems + index;
if (!PlainOldData)
new (item) ItemType(copyFrom);
else
*item = copyFrom;
return true;
}
inline bool _AddOrdered(const ItemType& copyFrom)
{
// special case
if (fCount == 0
|| fCompareItemsFunction(copyFrom, ItemAtFast(fCount - 1)) > 0) {
return _AddTail(copyFrom);
}
return _AddOrderedBounded(copyFrom, 0, fCount - 1);
}
inline bool _Resize(uint32 count)
@@ -352,6 +400,8 @@ private:
ItemType fNullItem;
uint32 fCount;
uint32 fAllocatedCount;
CompareItemFn fCompareItemsFunction;
CompareContextFn fCompareContextFunction;
};
+10 -76
View File
@@ -1060,39 +1060,20 @@ Model::_NotifyAuthorizationChanged()
}
/*! This method will find the stored 'DepotInfo' that correlates to the
supplied 'url' and will invoke the mapper function in order to get a
replacement for the 'DepotInfo'. The 'url' is a unique identifier
for the repository that holds across mirrors.
*/
void
Model::ForAllDepots(void (*func)(const DepotInfo& depot, void* context),
Model::ReplaceDepotByUrl(const BString& URL, DepotMapper* depotMapper,
void* context)
{
for (int32 i = 0; i < fDepots.CountItems(); i++) {
DepotInfo depotInfo = fDepots.ItemAtFast(i);
func(depotInfo, context);
}
}
/*! This method will find the stored 'DepotInfo' that correlates to the
supplied 'url' or 'baseUrl' and will invoke the mapper function in
order to get a replacement for the 'DepotInfo'. The two URLs are
different. The 'url' is a unique identifier for the repository that
holds across mirrors. The 'baseUrl' is the URL stem that was used
to access the repository data in the first place. The 'baseUrl' is
a legacy construct that exists from a time where the identifying
'url' was not being relayed properly.
*/
void
Model::ReplaceDepotByUrl(
const BString& URL,
const BString& baseURL,
// deprecated
DepotMapper* depotMapper, void* context)
{
for (int32 i = 0; i < fDepots.CountItems(); i++) {
DepotInfo depotInfo = fDepots.ItemAtFast(i);
if (RepositoryUrlUtils::EqualsOnUrlOrBaseUrl(URL, depotInfo.URL(),
baseURL, depotInfo.BaseURL())) {
if (RepositoryUrlUtils::EqualsNormalized(URL, depotInfo.URL())) {
BAutolock locker(&fLock);
fDepots.Replace(i, depotMapper->MapDepot(depotInfo, context));
}
@@ -1100,53 +1081,6 @@ Model::ReplaceDepotByUrl(
}
void
Model::ForAllPackages(PackageConsumer* packageConsumer, void* context)
{
for (int32 i = 0; i < fDepots.CountItems(); i++) {
DepotInfo depotInfo = fDepots.ItemAtFast(i);
PackageList packages = depotInfo.Packages();
for(int32 j = 0; j < packages.CountItems(); j++) {
const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j);
if (packageInfoRef != NULL) {
BAutolock locker(&fLock);
if (!packageConsumer->ConsumePackage(packageInfoRef, context))
return;
}
}
}
}
void
Model::ForPackageByNameInDepot(const BString& depotName,
const BString& packageName, PackageConsumer* packageConsumer, void* context)
{
int32 depotCount = fDepots.CountItems();
for (int32 i = 0; i < depotCount; i++) {
DepotInfo depotInfo = fDepots.ItemAtFast(i);
if (depotInfo.Name() == depotName) {
int32 packageIndex = depotInfo.PackageIndexByName(packageName);
if (-1 != packageIndex) {
PackageList packages = depotInfo.Packages();
const PackageInfoRef& packageInfoRef =
packages.ItemAtFast(packageIndex);
BAutolock locker(&fLock);
packageConsumer->ConsumePackage(packageInfoRef,
context);
}
return;
}
}
}
void
Model::LogDepotsWithNoWebAppRepositoryCode() const
{
@@ -1158,8 +1092,8 @@ Model::LogDepotsWithNoWebAppRepositoryCode() const
if (depot.WebAppRepositoryCode().Length() == 0) {
printf("depot [%s]", depot.Name().String());
if (depot.BaseURL().Length() > 0)
printf(" (%s)", depot.BaseURL().String());
if (depot.URL().Length() > 0)
printf(" (%s)", depot.URL().String());
printf(" correlates with no repository in the haiku"
"depot server system\n");
+1 -17
View File
@@ -9,9 +9,8 @@
#include <FindDirectory.h>
#include <Locker.h>
#include "AbstractServerProcess.h"
#include "AbstractProcess.h"
#include "LocalIconStore.h"
#include "BulkLoadContext.h"
#include "PackageInfo.h"
#include "WebAppInterface.h"
@@ -168,24 +167,9 @@ public:
void ReplaceDepotByUrl(
const BString& URL,
const BString& baseURL,
DepotMapper* depotMapper,
void* context);
void ForAllDepots(
void (*func)(const DepotInfo& depot,
void* context),
void* context);
void ForAllPackages(PackageConsumer* packageConsumer,
void* context);
void ForPackageByNameInDepot(
const BString& depotName,
const BString& packageName,
PackageConsumer* packageConsumer,
void* context);
status_t IconStoragePath(BPath& path) const;
status_t DumpExportRepositoryDataPath(BPath& path) const;
status_t DumpExportPkgDataPath(BPath& path,
+32 -32
View File
@@ -15,7 +15,6 @@
#include <Path.h>
// #pragma mark - UserInfo
@@ -1036,13 +1035,40 @@ PackageInfo::_NotifyListenersImmediate(uint32 changes)
}
// #pragma mark - Sorting Functions
/*! This function is used with the List class in order to facilitate fast
ordered inserting of packages.
*/
static int32
PackageCompare(const PackageInfoRef& p1, const PackageInfoRef& p2)
{
return p1->Name().Compare(p2->Name());
}
/*! This function is used with the List class in order to facilitate fast
searching of packages.
*/
static int32
PackageFixedNameCompare(const void* context,
const PackageInfoRef& package)
{
const BString* packageName = static_cast<const BString*>(context);
return packageName->Compare(package->Name());
}
// #pragma mark -
DepotInfo::DepotInfo()
:
fName(),
fPackages(),
fPackages(&PackageCompare, &PackageFixedNameCompare),
fWebAppRepositoryCode()
{
}
@@ -1051,7 +1077,7 @@ DepotInfo::DepotInfo()
DepotInfo::DepotInfo(const BString& name)
:
fName(name),
fPackages(),
fPackages(&PackageCompare, &PackageFixedNameCompare),
fWebAppRepositoryCode(),
fWebAppRepositorySourceCode()
{
@@ -1064,7 +1090,6 @@ DepotInfo::DepotInfo(const DepotInfo& other)
fPackages(other.fPackages),
fWebAppRepositoryCode(other.fWebAppRepositoryCode),
fWebAppRepositorySourceCode(other.fWebAppRepositorySourceCode),
fBaseURL(other.fBaseURL),
fURL(other.fURL)
{
}
@@ -1075,7 +1100,6 @@ DepotInfo::operator=(const DepotInfo& other)
{
fName = other.fName;
fPackages = other.fPackages;
fBaseURL = other.fBaseURL;
fURL = other.fURL;
fWebAppRepositoryCode = other.fWebAppRepositoryCode;
fWebAppRepositorySourceCode = other.fWebAppRepositorySourceCode;
@@ -1098,12 +1122,6 @@ DepotInfo::operator!=(const DepotInfo& other) const
}
static int32 PackageCompare(const PackageInfoRef& p1, const PackageInfoRef& p2)
{
return p1->Name().Compare(p2->Name());
}
/*! This method will insert the package into the list of packages
in order so that the list of packages remains in order.
*/
@@ -1111,23 +1129,14 @@ static int32 PackageCompare(const PackageInfoRef& p1, const PackageInfoRef& p2)
bool
DepotInfo::AddPackage(const PackageInfoRef& package)
{
return fPackages.AddOrdered(package, &PackageCompare);
}
static int32
PackageFixedNameCompare(const void* context,
const PackageInfoRef& package)
{
const BString* packageName = static_cast<const BString*>(context);
return packageName->Compare(package->Name());
return fPackages.Add(package);
}
int32
DepotInfo::PackageIndexByName(const BString& packageName)
DepotInfo::PackageIndexByName(const BString& packageName) const
{
return fPackages.BinarySearch(&packageName, &PackageFixedNameCompare);
return fPackages.Search(&packageName);
}
@@ -1142,8 +1151,6 @@ DepotInfo::SyncPackages(const PackageList& otherPackages)
for (int32 j = packages.CountItems() - 1; j >= 0; j--) {
const PackageInfoRef& package = packages.ItemAtFast(j);
if (package->Name() == otherPackage->Name()) {
// printf("%s: found package: '%s'\n", fName.String(),
// package->Name().String());
package->SetState(otherPackage->State());
package->SetLocalFilePath(otherPackage->LocalFilePath());
package->SetSystemDependency(
@@ -1169,13 +1176,6 @@ DepotInfo::SyncPackages(const PackageList& otherPackages)
}
void
DepotInfo::SetBaseURL(const BString& baseURL)
{
fBaseURL = baseURL;
}
void
DepotInfo::SetURL(const BString& URL)
{
+2 -8
View File
@@ -423,14 +423,11 @@ public:
bool AddPackage(const PackageInfoRef& package);
int32 PackageIndexByName(const BString& packageName);
int32 PackageIndexByName(const BString& packageName)
const;
void SyncPackages(const PackageList& packages);
void SetBaseURL(const BString& baseURL);
const BString& BaseURL() const
{ return fBaseURL; }
void SetURL(const BString& URL);
const BString& URL() const
{ return fURL; }
@@ -449,9 +446,6 @@ private:
PackageList fPackages;
BString fWebAppRepositoryCode;
BString fWebAppRepositorySourceCode;
BString fBaseURL;
// this is the URL at which the configured repository will be
// accessed to get data.
BString fURL;
// this is actually a unique identifier for the repository.
};
@@ -0,0 +1,155 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractProcess.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Locker.h>
#include "HaikuDepotConstants.h"
#include "Logger.h"
AbstractProcess::AbstractProcess()
:
fLock(),
fListener(NULL),
fWasStopped(false),
fProcessState(PROCESS_INITIAL),
fErrorStatus(B_OK)
{
}
AbstractProcess::~AbstractProcess()
{
}
void
AbstractProcess::SetListener(AbstractProcessListener* listener)
{
AutoLocker<BLocker> locker(&fLock);
fListener = listener;
}
status_t
AbstractProcess::Run()
{
{
AutoLocker<BLocker> locker(&fLock);
if (ProcessState() != PROCESS_INITIAL) {
printf("cannot start process as it is not idle");
return B_NOT_ALLOWED;
}
if (fWasStopped) {
printf("cannot start process as it was stopped");
return B_CANCELED;
}
fProcessState = PROCESS_RUNNING;
}
status_t runResult = RunInternal();
if (runResult != B_OK)
printf("[%s] an error has arisen; %s\n", Name(), strerror(runResult));
AbstractProcessListener* listener;
{
AutoLocker<BLocker> locker(&fLock);
fProcessState = PROCESS_COMPLETE;
fErrorStatus = runResult;
listener = fListener;
}
// this process may be part of a larger bulk-load process and
// if so, the process orchestration needs to know when this
// process has completed.
if (listener != NULL)
listener->ProcessExited();
return runResult;
}
bool
AbstractProcess::WasStopped()
{
AutoLocker<BLocker> locker(&fLock);
return fWasStopped;
}
status_t
AbstractProcess::ErrorStatus()
{
AutoLocker<BLocker> locker(&fLock);
return fErrorStatus;
}
/*! This method will stop the process. The actual process may carry on to
perform some tidy-ups on its thread so this does not stop the thread or
change the state of the process; just indicates to the running thread that
it should stop. If it has not yet been started then it will be put into
finished state.
*/
status_t
AbstractProcess::Stop()
{
status_t result = B_CANCELED;
AbstractProcessListener* listener = NULL;
{
AutoLocker<BLocker> locker(&fLock);
if (!fWasStopped) {
fWasStopped = true;
result = StopInternal();
if (fProcessState == PROCESS_INITIAL) {
listener = fListener;
fProcessState = PROCESS_COMPLETE;
}
}
}
if (listener != NULL)
listener->ProcessExited();
return result;
}
status_t
AbstractProcess::StopInternal()
{
return B_NOT_ALLOWED;
}
bool
AbstractProcess::IsRunning()
{
return ProcessState() == PROCESS_RUNNING;
}
process_state
AbstractProcess::ProcessState()
{
AutoLocker<BLocker> locker(&fLock);
return fProcessState;
}
@@ -0,0 +1,65 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_PROCESS_H
#define ABSTRACT_PROCESS_H
#include <String.h>
#include <Url.h>
#include "StandardMetaData.h"
#include "Stoppable.h"
typedef enum process_state {
PROCESS_INITIAL = 1 << 0,
PROCESS_RUNNING = 1 << 1,
PROCESS_COMPLETE = 1 << 2
} process_state;
/*! Clients are able to subclass from this 'interface' in order to accept
call-backs when a process has exited; either through success or through
failure.
*/
class AbstractProcessListener {
public:
virtual void ProcessExited() = 0;
};
/*! This is the superclass of all Processes. */
class AbstractProcess : public Stoppable {
public:
AbstractProcess();
virtual ~AbstractProcess();
virtual const char* Name() const = 0;
virtual const char* Description() const = 0;
status_t Run();
status_t Stop();
status_t ErrorStatus();
bool IsRunning();
bool WasStopped();
process_state ProcessState();
void SetListener(AbstractProcessListener* listener);
protected:
virtual status_t RunInternal() = 0;
virtual status_t StopInternal();
private:
BLocker fLock;
AbstractProcessListener*
fListener;
bool fWasStopped;
process_state fProcessState;
status_t fErrorStatus;
};
#endif // ABSTRACT_PROCESS_H
@@ -2,6 +2,8 @@
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractServerProcess.h"
#include <unistd.h>
@@ -9,10 +11,8 @@
#include <string.h>
#include <AutoDeleter.h>
#include <Autolock.h>
#include <FileIO.h>
#include <HttpTime.h>
#include <Locker.h>
#include <UrlProtocolRoster.h>
#include <support/ZlibCompressionAlgorithm.h>
@@ -34,14 +34,9 @@
#define TIMEOUT_MICROSECONDS 3e+7
AbstractServerProcess::AbstractServerProcess(
AbstractServerProcessListener* listener, uint32 options)
AbstractServerProcess::AbstractServerProcess(uint32 options)
:
fLock(),
fListener(listener),
fWasStopped(false),
fProcessState(SERVER_PROCESS_INITIAL),
fErrorStatus(B_OK),
AbstractProcess(),
fOptions(options),
fRequest(NULL)
{
@@ -69,60 +64,6 @@ AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready)
}
status_t
AbstractServerProcess::Run()
{
{
BAutolock locker(&fLock);
if (ProcessState() != SERVER_PROCESS_INITIAL) {
printf("cannot start server process as it is not idle");
return B_NOT_ALLOWED;
}
fProcessState = SERVER_PROCESS_RUNNING;
}
SetErrorStatus(RunInternal());
SetProcessState(SERVER_PROCESS_COMPLETE);
// this process may be part of a larger bulk-load process and
// if so, the process orchestration needs to know when this
// process has completed.
if (fListener != NULL)
fListener->ServerProcessExited();
return ErrorStatus();
}
bool
AbstractServerProcess::WasStopped()
{
BAutolock locker(&fLock);
return fWasStopped;
}
status_t
AbstractServerProcess::ErrorStatus()
{
BAutolock locker(&fLock);
return fErrorStatus;
}
status_t
AbstractServerProcess::Stop()
{
BAutolock locker(&fLock);
fWasStopped = true;
return StopInternal();
}
status_t
AbstractServerProcess::StopInternal()
{
@@ -130,41 +71,7 @@ AbstractServerProcess::StopInternal()
return fRequest->Stop();
}
return B_NOT_ALLOWED;
}
bool
AbstractServerProcess::IsRunning()
{
return ProcessState() == SERVER_PROCESS_RUNNING;
}
void
AbstractServerProcess::SetErrorStatus(status_t value)
{
BAutolock locker(&fLock);
if (fErrorStatus == B_OK) {
fErrorStatus = value;
}
}
void
AbstractServerProcess::SetProcessState(process_state value)
{
BAutolock locker(&fLock);
fProcessState = value;
}
process_state
AbstractServerProcess::ProcessState()
{
BAutolock locker(&fLock);
return fProcessState;
return AbstractProcess::StopInternal();
}
@@ -174,7 +81,11 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue) const
BPath metaDataPath;
BString jsonPath;
GetStandardMetaDataPath(metaDataPath);
status_t result = GetStandardMetaDataPath(metaDataPath);
if (result != B_OK)
return result;
GetStandardMetaDataJsonPath(jsonPath);
return IfModifiedSinceHeaderValue(headerValue, metaDataPath, jsonPath);
@@ -246,8 +157,8 @@ AbstractServerProcess::PopulateMetaData(
}
bool
AbstractServerProcess::LooksLikeGzip(const char *pathStr) const
/* static */ bool
AbstractServerProcess::LooksLikeGzip(const char *pathStr)
{
int l = strlen(pathStr);
return l > 4 && 0 == strncmp(&pathStr[l - 3], ".gz", 3);
@@ -268,7 +179,7 @@ AbstractServerProcess::ParseJsonFromFileWithListener(
FILE* file = fopen(pathStr, "rb");
if (file == NULL) {
fprintf(stderr, "unable to find the meta data file at [%s]\n",
printf("[%s] unable to find the meta data file at [%s]\n", Name(),
path.Path());
return B_FILE_NOT_FOUND;
}
@@ -348,17 +259,18 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
return B_CANCELED;
if (redirects > MAX_REDIRECTS) {
fprintf(stdout, "exceeded %d redirects --> failure\n", MAX_REDIRECTS);
printf("[%s] exceeded %d redirects --> failure\n", Name(),
MAX_REDIRECTS);
return B_IO_ERROR;
}
if (failures > MAX_FAILURES) {
fprintf(stdout, "exceeded %d failures\n", MAX_FAILURES);
printf("[%s] exceeded %d failures\n", Name(), MAX_FAILURES);
return B_IO_ERROR;
}
fprintf(stdout, "[%s] will stream '%s' to [%s]\n",
Name(), url.UrlString().String(), targetFilePath.Path());
printf("[%s] will stream '%s' to [%s]\n", Name(), url.UrlString().String(),
targetFilePath.Path());
ToFileUrlProtocolListener listener(targetFilePath, Name(),
Logger::IsTraceEnabled());
@@ -3,6 +3,7 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_SERVER_PROCESS_H
#define ABSTRACT_SERVER_PROCESS_H
@@ -11,54 +12,29 @@
#include <String.h>
#include <Url.h>
#include "AbstractProcess.h"
#include "StandardMetaData.h"
#include "Stoppable.h"
typedef enum process_options {
typedef enum server_process_options {
SERVER_PROCESS_NO_NETWORKING = 1 << 0,
SERVER_PROCESS_PREFER_CACHE = 1 << 1,
SERVER_PROCESS_DROP_CACHE = 1 << 2
} process_options;
} server_process_options;
typedef enum process_state {
SERVER_PROCESS_INITIAL = 1,
SERVER_PROCESS_RUNNING = 2,
SERVER_PROCESS_COMPLETE = 3
} process_state;
/*! This is the superclass of Processes that communicate with the Haiku Depot
Server (HDS) system.
*/
/*! Clients are able to subclass from this 'interface' in order to accept
call-backs when a process has exited; either through success or through
failure.
*/
class AbstractServerProcessListener {
class AbstractServerProcess : public AbstractProcess {
public:
virtual void ServerProcessExited() = 0;
};
class AbstractServerProcess : public Stoppable {
public:
AbstractServerProcess(
AbstractServerProcessListener* listener,
uint32 options);
AbstractServerProcess(uint32 options);
virtual ~AbstractServerProcess();
virtual const char* Name() = 0;
status_t Run();
status_t Stop();
status_t ErrorStatus();
bool IsRunning();
bool WasStopped();
protected:
virtual status_t RunInternal() = 0;
virtual status_t StopInternal();
virtual void GetStandardMetaDataPath(
virtual status_t GetStandardMetaDataPath(
BPath& path) const = 0;
virtual void GetStandardMetaDataJsonPath(
BString& jsonPath) const = 0;
@@ -92,27 +68,20 @@ protected:
static bool IsSuccess(status_t e);
protected:
virtual status_t StopInternal();
private:
BLocker fLock;
AbstractServerProcessListener*
fListener;
bool fWasStopped;
process_state fProcessState;
status_t fErrorStatus;
uint32 fOptions;
BHttpRequest* fRequest;
process_state ProcessState();
void SetErrorStatus(status_t value);
void SetProcessState(process_state value);
status_t DownloadToLocalFile(
const BPath& targetFilePath,
const BUrl& url,
uint32 redirects, uint32 failures);
bool LooksLikeGzip(const char *pathStr) const;
static bool LooksLikeGzip(const char *pathStr);
};
@@ -2,18 +2,21 @@
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractSingleFileServerProcess.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "ServerHelper.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
AbstractSingleFileServerProcess::AbstractSingleFileServerProcess(
AbstractServerProcessListener* listener, uint32 options)
uint32 options)
:
AbstractServerProcess(listener, options)
AbstractServerProcess(options)
{
}
@@ -29,9 +32,13 @@ AbstractSingleFileServerProcess::RunInternal()
if (Logger::IsInfoEnabled())
printf("[%s] will fetch data\n", Name());
BPath localPath = LocalPath();
BPath localPath;
status_t result = GetLocalPath(localPath);
if (result != B_OK)
return result;
BString urlPathComponent = UrlPathComponent();
status_t result = B_OK;
if (IsSuccess(result) && HasOption(SERVER_PROCESS_DROP_CACHE))
result = DeleteLocalFile(localPath);
@@ -48,6 +55,19 @@ AbstractSingleFileServerProcess::RunInternal()
result = DownloadToLocalFileAtomically(
localPath,
ServerSettings::CreateFullUrl(urlPathComponent));
if (!IsSuccess(result)) {
if (hasData) {
printf("[%s] failed to update data, but have old data "
"anyway so carry on with that\n", Name());
result = B_OK;
} else {
printf("[%s] failed to obtain data\n", Name());
}
} else {
if (Logger::IsInfoEnabled())
printf("[%s] did fetch data\n", Name());
}
}
if (IsSuccess(result)) {
@@ -61,11 +81,6 @@ AbstractSingleFileServerProcess::RunInternal()
}
if (IsSuccess(result)) {
if (Logger::IsInfoEnabled())
printf("[%s] did fetch data\n", Name());
// now load the data in and process it.
printf("[%s] will process data\n", Name());
result = ProcessLocalData();
@@ -82,3 +97,9 @@ AbstractSingleFileServerProcess::RunInternal()
return result;
}
status_t
AbstractSingleFileServerProcess::GetStandardMetaDataPath(BPath& path) const
{
return GetLocalPath(path);
}
@@ -1,8 +1,9 @@
/*
* Copyright 2017, Andrew Lindesay <[email protected]>.
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_SINGLE_FILE_SERVER_PROCESS_H
#define ABSTRACT_SINGLE_FILE_SERVER_PROCESS_H
@@ -12,9 +13,7 @@
class AbstractSingleFileServerProcess : public AbstractServerProcess {
public:
AbstractSingleFileServerProcess(
AbstractServerProcessListener* listener,
uint32 options);
AbstractSingleFileServerProcess(uint32 options);
virtual ~AbstractSingleFileServerProcess();
protected:
@@ -24,7 +23,9 @@ protected:
virtual BString UrlPathComponent() = 0;
virtual BPath& LocalPath() = 0;
virtual status_t GetLocalPath(BPath& path) const = 0;
virtual status_t GetStandardMetaDataPath(BPath& path) const;
};
#endif // ABSTRACT_SINGLE_FILE_SERVER_PROCESS_H
@@ -1,131 +0,0 @@
/*
* Copyright 2017, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "BulkLoadContext.h"
BulkLoadContext::BulkLoadContext()
:
fState(BULK_LOAD_INITIAL),
fIconProcess(NULL),
fRepositoryProcess(NULL),
fPkgProcesses(new List<AbstractServerProcess*, true>()),
fProcessOptions(0)
{
}
BulkLoadContext::~BulkLoadContext()
{
StopAllProcesses();
if (fIconProcess != NULL)
delete fIconProcess;
if (fRepositoryProcess != NULL)
delete fRepositoryProcess;
int32 count = fPkgProcesses->CountItems();
int32 i;
for (i = 0; i < count; i++)
delete fPkgProcesses->ItemAt(i);
delete fPkgProcesses;
}
bulk_load_state
BulkLoadContext::State()
{
return fState;
}
void
BulkLoadContext::SetState(bulk_load_state value)
{
fState = value;
}
void
BulkLoadContext::StopAllProcesses()
{
if (fIconProcess != NULL)
fIconProcess->Stop();
if (fRepositoryProcess != NULL)
fRepositoryProcess->Stop();
int32 count = fPkgProcesses->CountItems();
int32 i;
for (i = 0; i < count; i++)
fPkgProcesses->ItemAt(i)->Stop();
}
AbstractServerProcess*
BulkLoadContext::IconProcess()
{
return fIconProcess;
}
void
BulkLoadContext::SetIconProcess(AbstractServerProcess* value)
{
fIconProcess = value;
}
AbstractServerProcess*
BulkLoadContext::RepositoryProcess()
{
return fRepositoryProcess;
}
void
BulkLoadContext::SetRepositoryProcess(
AbstractServerProcess* value)
{
fRepositoryProcess = value;
}
int32
BulkLoadContext::CountPkgProcesses()
{
return fPkgProcesses->CountItems();
}
AbstractServerProcess*
BulkLoadContext::PkgProcessAt(int32 index)
{
return fPkgProcesses->ItemAt(index);
}
void
BulkLoadContext::AddPkgProcess(AbstractServerProcess *value)
{
fPkgProcesses->Add(value);
}
void
BulkLoadContext::AddProcessOption(uint32 flag)
{
fProcessOptions = fProcessOptions | flag;
}
uint32
BulkLoadContext::ProcessOptions()
{
return fProcessOptions;
}
@@ -1,66 +0,0 @@
/*
* Copyright 2017, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef BULK_LOAD_CONTEXT_H
#define BULK_LOAD_CONTEXT_H
#include <String.h>
#include <File.h>
#include <Path.h>
#include "AbstractServerProcess.h"
#include "List.h"
typedef enum bulk_load_state {
BULK_LOAD_INITIAL = 1,
BULK_LOAD_REPOSITORY_AND_REFERENCE = 2,
BULK_LOAD_PKGS_AND_ICONS = 3,
BULK_LOAD_COMPLETE = 4
} bulk_load_state;
class BulkLoadContext {
public:
BulkLoadContext();
virtual ~BulkLoadContext();
void StopAllProcesses();
bulk_load_state State();
void SetState(bulk_load_state value);
AbstractServerProcess*
IconProcess();
void SetIconProcess(AbstractServerProcess* value);
AbstractServerProcess*
RepositoryProcess();
void SetRepositoryProcess(
AbstractServerProcess* value);
int32 CountPkgProcesses();
AbstractServerProcess*
PkgProcessAt(int32 index);
void AddPkgProcess(AbstractServerProcess *value);
void AddProcessOption(uint32 flag);
uint32 ProcessOptions();
private:
bulk_load_state
fState;
AbstractServerProcess*
fIconProcess;
AbstractServerProcess*
fRepositoryProcess;
List<AbstractServerProcess*, true>*
fPkgProcesses;
uint32 fProcessOptions;
};
#endif // BULK_LOAD_CONTEXT_H
@@ -1,358 +0,0 @@
/*
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "BulkLoadStateMachine.h"
#include <Autolock.h>
#include "Logger.h"
#include "PkgDataUpdateProcess.h"
#include "RepositoryDataUpdateProcess.h"
#include "ServerIconExportUpdateProcess.h"
#include "ServerSettings.h"
#include "ServerHelper.h"
BulkLoadStateMachine::BulkLoadStateMachine(Model* model)
:
fBulkLoadContext(NULL),
fModel(model)
{
}
BulkLoadStateMachine::~BulkLoadStateMachine()
{
Stop();
}
bool
BulkLoadStateMachine::IsRunning()
{
BAutolock locker(&fLock);
return fBulkLoadContext != NULL;
}
/*! This gets invoked when one of the background processes has exited. */
void
BulkLoadStateMachine::ServerProcessExited()
{
ContextPoll();
}
static const char* bulk_load_state_name(bulk_load_state state) {
switch(state) {
case BULK_LOAD_INITIAL:
return "BULK_LOAD_INITIAL";
case BULK_LOAD_REPOSITORY_AND_REFERENCE:
return "BULK_LOAD_REPOSITORY_AND_REFERENCE";
case BULK_LOAD_PKGS_AND_ICONS:
return "BULK_LOAD_PKGS_AND_ICONS";
case BULK_LOAD_COMPLETE:
return "BULK_LOAD_COMPLETE";
default:
return "???";
}
}
void
BulkLoadStateMachine::SetContextState(bulk_load_state state)
{
if (Logger::IsDebugEnabled()) {
printf("bulk load - transition to state [%s]\n",
bulk_load_state_name(state));
}
fBulkLoadContext->SetState(state);
}
/*! Bulk loading data into the model can be considered to be a state
machine. This method is invoked each time that an event state
change happens.
*/
void
BulkLoadStateMachine::ContextPoll()
{
BAutolock locker(&fLock);
if (Logger::IsDebugEnabled())
printf("bulk load - context poll\n");
if (CanTransitionTo(BULK_LOAD_REPOSITORY_AND_REFERENCE)) {
SetContextState(BULK_LOAD_REPOSITORY_AND_REFERENCE);
InitiateBulkPopulateIcons();
if (InitiateBulkRepositories() != B_OK)
ContextPoll();
return;
}
if (CanTransitionTo(BULK_LOAD_PKGS_AND_ICONS)) {
fModel->LogDepotsWithNoWebAppRepositoryCode();
SetContextState(BULK_LOAD_PKGS_AND_ICONS);
InitiateBulkPopulatePackagesForAllDepots();
return;
}
if (CanTransitionTo(BULK_LOAD_COMPLETE)) {
SetContextState(BULK_LOAD_COMPLETE);
delete fBulkLoadContext;
fBulkLoadContext = NULL;
return;
}
}
bool
BulkLoadStateMachine::CanTransitionTo(bulk_load_state targetState)
{
if (fBulkLoadContext != NULL) {
bulk_load_state existingState = fBulkLoadContext->State();
switch (targetState) {
case BULK_LOAD_INITIAL:
return false;
case BULK_LOAD_REPOSITORY_AND_REFERENCE:
return existingState == BULK_LOAD_INITIAL;
case BULK_LOAD_PKGS_AND_ICONS:
return (existingState == BULK_LOAD_REPOSITORY_AND_REFERENCE)
&& ((fBulkLoadContext->RepositoryProcess() == NULL)
|| !fBulkLoadContext->RepositoryProcess()->IsRunning());
case BULK_LOAD_COMPLETE:
if ((existingState == BULK_LOAD_PKGS_AND_ICONS)
&& ((fBulkLoadContext->IconProcess() == NULL)
|| !fBulkLoadContext->IconProcess()->IsRunning())) {
int32 i;
for (i = 0; i < fBulkLoadContext->CountPkgProcesses(); i++) {
AbstractServerProcess* process =
fBulkLoadContext->PkgProcessAt(i);
if (process->IsRunning())
return false;
}
return true;
}
break;
}
}
return false;
}
void
BulkLoadStateMachine::StopAllProcesses()
{
BAutolock locker(&fLock);
if (fBulkLoadContext != NULL) {
if (NULL != fBulkLoadContext->IconProcess())
fBulkLoadContext->IconProcess()->Stop();
if (NULL != fBulkLoadContext->RepositoryProcess())
fBulkLoadContext->RepositoryProcess()->Stop();
int32 i;
for(i = 0; i < fBulkLoadContext->CountPkgProcesses(); i++) {
AbstractServerProcess* serverProcess =
fBulkLoadContext->PkgProcessAt(i);
serverProcess->Stop();
}
}
}
void
BulkLoadStateMachine::Start()
{
if (Logger::IsInfoEnabled())
printf("bulk load - start\n");
Stop();
{
BAutolock locker(&fLock);
if (!IsRunning()) {
fBulkLoadContext = new BulkLoadContext();
if (ServerSettings::IsClientTooOld()) {
printf("bulk load proceeding without network communications "
"because the client is too old\n");
fBulkLoadContext->AddProcessOption(
SERVER_PROCESS_NO_NETWORKING);
}
if (!ServerHelper::IsNetworkAvailable()) {
fBulkLoadContext->AddProcessOption(
SERVER_PROCESS_NO_NETWORKING);
}
if (ServerSettings::PreferCache())
fBulkLoadContext->AddProcessOption(SERVER_PROCESS_PREFER_CACHE);
if (ServerSettings::DropCache())
fBulkLoadContext->AddProcessOption(SERVER_PROCESS_DROP_CACHE);
ContextPoll();
}
}
}
void
BulkLoadStateMachine::Stop()
{
StopAllProcesses();
// spin lock to wait for the bulk-load processes to complete.
while (IsRunning())
snooze(500000);
}
/*! This method is the initial function that is invoked on starting a new
thread. It will start a server process that is part of the bulk-load.
*/
status_t
BulkLoadStateMachine::StartProcess(void* cookie)
{
AbstractServerProcess* process =
static_cast<AbstractServerProcess*>(cookie);
if (Logger::IsInfoEnabled()) {
printf("bulk load - starting process [%s]\n",
process->Name());
}
process->Run();
return B_OK;
}
status_t
BulkLoadStateMachine::InitiateServerProcess(AbstractServerProcess* process)
{
if (Logger::IsInfoEnabled())
printf("bulk load - initiating [%s]\n", process->Name());
thread_id tid = spawn_thread(&StartProcess,
process->Name(), B_NORMAL_PRIORITY, process);
if (tid >= 0) {
resume_thread(tid);
return B_OK;
}
return B_ERROR;
}
status_t
BulkLoadStateMachine::InitiateBulkRepositories()
{
status_t result = B_OK;
BPath dataPath;
fBulkLoadContext->SetRepositoryProcess(NULL);
result = fModel->DumpExportRepositoryDataPath(dataPath);
if (result != B_OK) {
BAutolock locker(&fLock);
printf("unable to obtain the path for storing the repository data\n");
ContextPoll();
return B_ERROR;
}
fBulkLoadContext->SetRepositoryProcess(
new RepositoryDataUpdateProcess(this, dataPath, fModel,
fBulkLoadContext->ProcessOptions()));
return InitiateServerProcess(fBulkLoadContext->RepositoryProcess());
}
status_t
BulkLoadStateMachine::InitiateBulkPopulateIcons()
{
BPath path;
if (fModel->IconStoragePath(path) != B_OK) {
BAutolock locker(&fLock);
printf("unable to obtain the path for storing icons\n");
ContextPoll();
return B_ERROR;
}
AbstractServerProcess *process = new ServerIconExportUpdateProcess(
this, path, fModel, fBulkLoadContext->ProcessOptions());
fBulkLoadContext->SetIconProcess(process);
return InitiateServerProcess(process);
}
status_t
BulkLoadStateMachine::InitiateBulkPopulatePackagesForDepot(
const DepotInfo& depotInfo)
{
BString repositorySourceCode = depotInfo.WebAppRepositorySourceCode();
if (repositorySourceCode.Length() == 0) {
printf("the depot [%s] has no repository source code\n",
depotInfo.Name().String());
return B_ERROR;
}
BPath repositorySourcePkgDataPath;
if (fModel->DumpExportPkgDataPath(repositorySourcePkgDataPath,
repositorySourceCode) != B_OK) {
BAutolock locker(&fLock);
printf("unable to obtain the path for storing data for [%s]\n",
repositorySourceCode.String());
ContextPoll();
return B_ERROR;
}
AbstractServerProcess *process = new PkgDataUpdateProcess(
this, repositorySourcePkgDataPath, fModel->PreferredLanguage(),
repositorySourceCode, depotInfo.Name(), fModel,
fBulkLoadContext->ProcessOptions());
fBulkLoadContext->AddPkgProcess(process);
return InitiateServerProcess(process);
}
// static
void
BulkLoadStateMachine::InitiatePopulatePackagesForDepotCallback(
const DepotInfo& depotInfo, void* context)
{
BulkLoadStateMachine* stateMachine =
static_cast<BulkLoadStateMachine*>(context);
stateMachine->InitiateBulkPopulatePackagesForDepot(depotInfo);
}
void
BulkLoadStateMachine::InitiateBulkPopulatePackagesForAllDepots()
{
fModel->ForAllDepots(&InitiatePopulatePackagesForDepotCallback, this);
printf("did initiate populate package data for %" B_PRId32 " depots\n",
fBulkLoadContext->CountPkgProcesses());
if (0 == fBulkLoadContext->CountPkgProcesses())
ContextPoll();
}
@@ -1,59 +0,0 @@
/*
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef BULK_LOAD_STATE_MACHINE_H
#define BULK_LOAD_STATE_MACHINE_H
#include <String.h>
#include <File.h>
#include <Locker.h>
#include <Path.h>
#include "AbstractServerProcess.h"
#include "Model.h"
class BulkLoadStateMachine : public AbstractServerProcessListener {
public:
BulkLoadStateMachine(Model* model);
virtual ~BulkLoadStateMachine();
bool IsRunning();
void Start();
void Stop();
void ServerProcessExited();
private:
static status_t StartProcess(void* cookie);
void ContextPoll();
void SetContextState(bulk_load_state state);
void StopAllProcesses();
bool CanTransitionTo(
bulk_load_state targetState);
static void InitiatePopulatePackagesForDepotCallback(
const DepotInfo& depotInfo,
void* context);
status_t InitiateServerProcess(
AbstractServerProcess* process);
status_t InitiateBulkRepositories();
status_t InitiateBulkPopulateIcons();
status_t InitiateBulkPopulatePackagesForDepot(
const DepotInfo& depotInfo);
void InitiateBulkPopulatePackagesForAllDepots();
private:
BLocker fLock;
BulkLoadContext* fBulkLoadContext;
Model* fModel;
};
#endif // BULK_LOAD_STATE_MACHINE_H
@@ -0,0 +1,393 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LocalPkgDataLoadProcess.h"
#include <map>
#include <vector>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Roster.h>
#include <StringList.h>
#include "AppUtils.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "PackageInfo.h"
#include "PackageManager.h"
#include "RepositoryUrlUtils.h"
#include <package/Context.h>
#include <package/manager/Exceptions.h>
#include <package/manager/RepositoryBuilder.h>
#include <package/PackageRoster.h>
#include "package/RepositoryCache.h"
#include <package/RefreshRepositoryRequest.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverResult.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LocalPkgDataLoadProcess"
using namespace BPackageKit;
using namespace BPackageKit::BManager::BPrivate;
typedef std::map<BString, PackageInfoRef> PackageInfoMap;
/*!
\param packageInfoListener is assigned to each package model object.
*/
LocalPkgDataLoadProcess::LocalPkgDataLoadProcess(
PackageInfoListener* packageInfoListener,
Model *model, bool force)
:
AbstractProcess(),
fModel(model),
fForce(force),
fPackageInfoListener(packageInfoListener)
{
}
LocalPkgDataLoadProcess::~LocalPkgDataLoadProcess()
{
}
const char*
LocalPkgDataLoadProcess::Name() const
{
return "LocalPkgDataLoadProcess";
}
const char*
LocalPkgDataLoadProcess::Description() const
{
return B_TRANSLATE("Reading repository data");
}
/*! The contents of this method implementation have been 'lifted and shifted'
from MainWindow.cpp in order that the logic fits into the background
loading processes. The code needs to be broken up into methods with some
sort of a state object carrying the state of the process. As part of this,
better error handling and error reporting would also be advantageous.
*/
status_t
LocalPkgDataLoadProcess::RunInternal()
{
if (Logger::IsDebugEnabled())
printf("[%s] will refresh the package list\n", Name());
BPackageRoster roster;
BStringList repositoryNames;
status_t result = roster.GetRepositoryNames(repositoryNames);
if (result != B_OK)
return result;
std::vector<DepotInfo> depots(repositoryNames.CountStrings());
for (int32 i = 0; i < repositoryNames.CountStrings(); i++) {
const BString& repoName = repositoryNames.StringAt(i);
DepotInfo depotInfo = DepotInfo(repoName);
BRepositoryConfig repoConfig;
status_t getRepositoryConfigStatus = roster.GetRepositoryConfig(
repoName, &repoConfig);
if (getRepositoryConfigStatus == B_OK) {
depotInfo.SetURL(repoConfig.URL());
if (Logger::IsDebugEnabled()) {
printf("[%s] local repository [%s] info;\n"
" * url [%s]\n", Name(), repoName.String(),
repoConfig.URL().String());
}
} else {
printf("[%s] unable to obtain the repository config for local "
"repository '%s'; %s\n", Name(),
repoName.String(), strerror(getRepositoryConfigStatus));
}
depots[i] = depotInfo;
}
PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
try {
manager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
| PackageManager::B_ADD_REMOTE_REPOSITORIES);
} catch (BException ex) {
BString message(B_TRANSLATE("An error occurred while "
"initializing the package manager: %message%"));
message.ReplaceFirst("%message%", ex.Message());
_NotifyError(message.String());
return B_ERROR;
}
BObjectList<BSolverPackage> packages;
result = manager.Solver()->FindPackages("",
BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
| BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
| BSolver::B_FIND_IN_PROVIDES,
packages);
if (result != B_OK) {
BString message(B_TRANSLATE("An error occurred while "
"obtaining the package list: %message%"));
message.ReplaceFirst("%message%", strerror(result));
_NotifyError(message.String());
return B_ERROR;
}
if (packages.IsEmpty())
return B_ERROR;
PackageInfoMap foundPackages;
// if a given package is installed locally, we will potentially
// get back multiple entries, one for each local installation
// location, and one for each remote repository the package
// is available in. The above map is used to ensure that in such
// cases we consolidate the information, rather than displaying
// duplicates
PackageInfoMap remotePackages;
// any package that we find in a remote repository goes in this map.
// this is later used to discern which packages came from a local
// installation only, as those must be handled a bit differently
// upon uninstallation, since we'd no longer be able to pull them
// down remotely.
BStringList systemFlaggedPackages;
// any packages flagged as a system package are added to this list.
// such packages cannot be uninstalled, nor can any of their deps.
PackageInfoMap systemInstalledPackages;
// any packages installed in system are added to this list.
// This is later used for dependency resolution of the actual
// system packages in order to compute the list of protected
// dependencies indicated above.
for (int32 i = 0; i < packages.CountItems(); i++) {
BSolverPackage* package = packages.ItemAt(i);
const BPackageInfo& repoPackageInfo = package->Info();
const BString repositoryName = package->Repository()->Name();
PackageInfoRef modelInfo;
PackageInfoMap::iterator it = foundPackages.find(
repoPackageInfo.Name());
if (it != foundPackages.end())
modelInfo.SetTo(it->second);
else {
// Add new package info
modelInfo.SetTo(new(std::nothrow) PackageInfo(repoPackageInfo),
true);
if (modelInfo.Get() == NULL)
return B_ERROR;
foundPackages[repoPackageInfo.Name()] = modelInfo;
}
// The package list here considers those packages that are installed
// in the system as well as those that exist in remote repositories.
// It is better if the 'depot name' is from the remote repository
// because then it will be possible to perform a rating on it later.
if (modelInfo->DepotName().IsEmpty()
|| modelInfo->DepotName() == REPOSITORY_NAME_SYSTEM
|| modelInfo->DepotName() == REPOSITORY_NAME_INSTALLED) {
modelInfo->SetDepotName(repositoryName);
}
modelInfo->AddListener(fPackageInfoListener);
BSolverRepository* repository = package->Repository();
BPackageManager::RemoteRepository* remoteRepository =
dynamic_cast<BPackageManager::RemoteRepository*>(repository);
if (remoteRepository != NULL) {
std::vector<DepotInfo>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (RepositoryUrlUtils::EqualsNormalized(
it->URL(), remoteRepository->Config().URL())) {
break;
}
}
if (it == depots.end()) {
if (Logger::IsDebugEnabled()) {
printf("pkg [%s] repository [%s] not recognized"
" --> ignored\n",
modelInfo->Name().String(), repositoryName.String());
}
} else {
it->AddPackage(modelInfo);
if (Logger::IsTraceEnabled()) {
printf("pkg [%s] assigned to [%s]\n",
modelInfo->Name().String(), repositoryName.String());
}
}
remotePackages[modelInfo->Name()] = modelInfo;
} else {
if (repository == static_cast<const BSolverRepository*>(
manager.SystemRepository())) {
modelInfo->AddInstallationLocation(
B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
if (!modelInfo->IsSystemPackage()) {
systemInstalledPackages[repoPackageInfo.FileName()]
= modelInfo;
}
} else if (repository == static_cast<const BSolverRepository*>(
manager.HomeRepository())) {
modelInfo->AddInstallationLocation(
B_PACKAGE_INSTALLATION_LOCATION_HOME);
}
}
if (modelInfo->IsSystemPackage())
systemFlaggedPackages.Add(repoPackageInfo.FileName());
}
BAutolock lock(fModel->Lock());
if (fForce)
fModel->Clear();
// filter remote packages from the found list
// any packages remaining will be locally installed packages
// that weren't acquired from a repository
for (PackageInfoMap::iterator it = remotePackages.begin();
it != remotePackages.end(); it++) {
foundPackages.erase(it->first);
}
if (!foundPackages.empty()) {
BString repoName = B_TRANSLATE("Local");
depots.push_back(DepotInfo(repoName));
for (PackageInfoMap::iterator it = foundPackages.begin();
it != foundPackages.end(); ++it) {
depots.back().AddPackage(it->second);
}
}
{
std::vector<DepotInfo>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (fModel->HasDepot(it->Name()))
fModel->SyncDepot(*it);
else
fModel->AddDepot(*it);
}
}
// compute the OS package dependencies
try {
// create the solver
BSolver* solver;
status_t error = BSolver::Create(solver);
if (error != B_OK)
throw BFatalErrorException(error, "Failed to create solver.");
ObjectDeleter<BSolver> solverDeleter(solver);
BPath systemPath;
error = find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &systemPath);
if (error != B_OK) {
throw BFatalErrorException(error,
"Unable to retrieve system packages directory.");
}
// add the "installed" repository with the given packages
BSolverRepository installedRepository;
{
BRepositoryBuilder installedRepositoryBuilder(installedRepository,
REPOSITORY_NAME_INSTALLED);
for (int32 i = 0; i < systemFlaggedPackages.CountStrings(); i++) {
BPath packagePath(systemPath);
packagePath.Append(systemFlaggedPackages.StringAt(i));
installedRepositoryBuilder.AddPackage(packagePath.Path());
}
installedRepositoryBuilder.AddToSolver(solver, true);
}
// add system repository
BSolverRepository systemRepository;
{
BRepositoryBuilder systemRepositoryBuilder(systemRepository,
REPOSITORY_NAME_SYSTEM);
for (PackageInfoMap::iterator it = systemInstalledPackages.begin();
it != systemInstalledPackages.end(); it++) {
BPath packagePath(systemPath);
packagePath.Append(it->first);
systemRepositoryBuilder.AddPackage(packagePath.Path());
}
systemRepositoryBuilder.AddToSolver(solver, false);
}
// solve
error = solver->VerifyInstallation();
if (error != B_OK) {
throw BFatalErrorException(error, "Failed to compute packages to "
"install.");
}
BSolverResult solverResult;
error = solver->GetResult(solverResult);
if (error != B_OK) {
throw BFatalErrorException(error, "Failed to retrieve system "
"package dependency list.");
}
for (int32 i = 0; const BSolverResultElement* element
= solverResult.ElementAt(i); i++) {
BSolverPackage* package = element->Package();
if (element->Type() == BSolverResultElement::B_TYPE_INSTALL) {
PackageInfoMap::iterator it = systemInstalledPackages.find(
package->Info().FileName());
if (it != systemInstalledPackages.end())
it->second->SetSystemDependency(true);
}
}
} catch (BFatalErrorException ex) {
printf("Fatal exception occurred while resolving system dependencies: "
"%s, details: %s\n", strerror(ex.Error()), ex.Details().String());
} catch (BNothingToDoException) {
// do nothing
} catch (BException ex) {
printf("Exception occurred while resolving system dependencies: %s\n",
ex.Message().String());
} catch (...) {
printf("Unknown exception occurred while resolving system "
"dependencies.\n");
}
if (Logger::IsDebugEnabled())
printf("did refresh the package list\n");
return B_OK;
}
void
LocalPkgDataLoadProcess::_NotifyError(const BString& messageText) const
{
printf("an error has arisen loading data of packages from local : %s\n",
messageText.String());
AppUtils::NotifySimpleError(
B_TRANSLATE("Local Repository Load Error"),
messageText);
}
@@ -0,0 +1,55 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LOCAL_PKG_DATA_LOAD_PROCESS__H
#define LOCAL_PKG_DATA_LOAD_PROCESS__H
#include "AbstractProcess.h"
#include "Model.h"
#include "PackageInfo.h"
#include <File.h>
#include <Path.h>
#include <String.h>
#include <Url.h>
#include <package/RepositoryCache.h>
class PkgDataLoadState;
/*! This process will take the data from the locally stored repositories (HPKR)
and will extract the packages. The packages are then loaded into the
HaikuDepot 'Model'.
*/
class LocalPkgDataLoadProcess : public AbstractProcess {
public:
LocalPkgDataLoadProcess(
PackageInfoListener* packageInfoListener,
Model *model, bool force = false);
virtual ~LocalPkgDataLoadProcess();
const char* Name() const;
const char* Description() const;
protected:
virtual status_t RunInternal();
private:
void _NotifyError(const BString& messageText) const;
private:
Model* fModel;
bool fForce;
PackageInfoListener*
fPackageInfoListener;
};
#endif // LOCAL_PKG_DATA_LOAD_PROCESS__H
@@ -0,0 +1,156 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LocalRepositoryUpdateProcess.h"
#include <Catalog.h>
#include <Roster.h>
#include <String.h>
#include <StringList.h>
#include <package/Context.h>
#include <package/manager/Exceptions.h>
#include <package/PackageRoster.h>
#include <package/RefreshRepositoryRequest.h>
#include "AppUtils.h"
#include "DecisionProvider.h"
#include "JobStateListener.h"
#include "Logger.h"
#include "HaikuDepotConstants.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LocalRepositoryUpdateProcess"
using namespace BPackageKit;
using namespace BPackageKit::BManager::BPrivate;
LocalRepositoryUpdateProcess::LocalRepositoryUpdateProcess(
Model *model, bool force)
:
AbstractProcess(),
fModel(model),
fForce(force)
{
}
LocalRepositoryUpdateProcess::~LocalRepositoryUpdateProcess()
{
}
const char*
LocalRepositoryUpdateProcess::Name() const
{
return "LocalRepositoryUpdateProcess";
}
const char*
LocalRepositoryUpdateProcess::Description() const
{
return B_TRANSLATE("Fetching remote repository data");
}
status_t
LocalRepositoryUpdateProcess::RunInternal()
{
BPackageRoster roster;
BStringList repoNames;
if (Logger::IsInfoEnabled()) {
printf("[%s] will update local repositories\n", Name());
}
status_t result = roster.GetRepositoryNames(repoNames);
if (result == B_OK) {
DecisionProvider decisionProvider;
JobStateListener listener;
BContext context(decisionProvider, listener);
BRepositoryCache cache;
for (
int32 i = 0;
result == B_OK && i < repoNames.CountStrings() && !WasStopped();
++i) {
result = _RunForRepositoryName(repoNames.StringAt(i), context,
roster, &cache);
}
} else {
_NotifyError(strerror(result));
result = B_ERROR;
}
if (result == B_OK && Logger::IsInfoEnabled()) {
printf("[%s] did update %" B_PRIi32 " local repositories\n",
Name(), repoNames.CountStrings());
}
return result;
}
status_t
LocalRepositoryUpdateProcess::_RunForRepositoryName(const BString& repoName,
BPackageKit::BContext& context, BPackageKit::BPackageRoster& roster,
BPackageKit::BRepositoryCache* cache)
{
status_t result = B_ERROR;
BRepositoryConfig repoConfig;
result = roster.GetRepositoryConfig(repoName, &repoConfig);
if (result == B_OK) {
if (roster.GetRepositoryCache(repoName, cache) != B_OK || fForce) {
try {
BRefreshRepositoryRequest refreshRequest(context, repoConfig);
result = refreshRequest.Process();
result = B_OK;
} catch (BFatalErrorException ex) {
_NotifyError(ex.Message(), ex.Details());
} catch (BException ex) {
_NotifyError(ex.Message());
}
}
} else {
_NotifyError(strerror(result));
}
return result;
}
void
LocalRepositoryUpdateProcess::_NotifyError(const BString& error) const
{
_NotifyError(error, "");
}
void
LocalRepositoryUpdateProcess::_NotifyError(const BString& error,
const BString& details) const
{
printf("an error has arisen updating the local repositories : %s\n",
error.String());
BString alertText(B_TRANSLATE("An error occurred while refreshing the "
"repository: %error%"));
alertText.ReplaceFirst("%error%", error);
if (!details.IsEmpty()) {
alertText.Append(" (");
alertText.Append(details);
alertText.Append(")");
}
AppUtils::NotifySimpleError(
B_TRANSLATE("Repository Update Error"),
alertText);
}
@@ -0,0 +1,59 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LOCAL_REPOSITORY_UPDATE_PROCESS__H
#define LOCAL_REPOSITORY_UPDATE_PROCESS__H
#include "AbstractProcess.h"
#include <File.h>
#include <Path.h>
#include <String.h>
#include <Url.h>
#include <package/Context.h>
#include <package/PackageRoster.h>
#include <package/RepositoryCache.h>
#include "Model.h"
#include "PackageInfo.h"
/*! This process is intended to, for each repository configured on the host,
retrieve an updated set of data for the repository from the remote
repository site. This is typically HPKR data copied over to the local
machine. From there, a latter process will process this data by using the
facilities of the Package Kit.
*/
class LocalRepositoryUpdateProcess : public AbstractProcess {
public:
LocalRepositoryUpdateProcess(
Model *model, bool force = false);
virtual ~LocalRepositoryUpdateProcess();
const char* Name() const;
const char* Description() const;
protected:
virtual status_t RunInternal();
private:
status_t _RunForRepositoryName(const BString& repoName,
BPackageKit::BContext& context,
BPackageKit::BPackageRoster& roster,
BPackageKit::BRepositoryCache* cache);
void _NotifyError(const BString& error) const;
void _NotifyError(const BString& error,
const BString& details) const;
private:
Model* fModel;
bool fForce;
};
#endif // LOCAL_REPOSITORY_UPDATE_PROCESS__H
@@ -0,0 +1,326 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ProcessCoordinator.h"
#include <AutoLocker.h>
#include <Catalog.h>
#include <StringFormat.h>
#include "Logger.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ProcessCoordinator"
// #pragma mark - ProcessCoordinatorState implementation
ProcessCoordinatorState::ProcessCoordinatorState(
const ProcessCoordinator* processCoordinator, float progress,
const BString& message, bool isRunning, status_t errorStatus)
:
fProcessCoordinator(processCoordinator),
fProgress(progress),
fMessage(message),
fIsRunning(isRunning),
fErrorStatus(errorStatus)
{
}
ProcessCoordinatorState::~ProcessCoordinatorState()
{
}
const ProcessCoordinator*
ProcessCoordinatorState::Coordinator() const
{
return fProcessCoordinator;
}
float
ProcessCoordinatorState::Progress() const
{
return fProgress;
}
BString
ProcessCoordinatorState::Message() const
{
return fMessage;
}
bool
ProcessCoordinatorState::IsRunning() const
{
return fIsRunning;
}
status_t
ProcessCoordinatorState::ErrorStatus() const
{
return fErrorStatus;
}
// #pragma mark - ProcessCoordinator implementation
ProcessCoordinator::ProcessCoordinator(ProcessCoordinatorListener* listener)
:
fListener(listener),
fWasStopped(false)
{
}
ProcessCoordinator::~ProcessCoordinator()
{
AutoLocker<BLocker> locker(&fLock);
for (int32 i = 0; i < fNodes.CountItems(); i++) {
ProcessNode* node = fNodes.ItemAt(i);
node->Process()->SetListener(NULL);
delete node;
}
}
void
ProcessCoordinator::AddNode(ProcessNode* node)
{
AutoLocker<BLocker> locker(&fLock);
fNodes.Add(node);
node->Process()->SetListener(this);
}
void
ProcessCoordinator::ProcessExited()
{
_CoordinateAndCallListener();
}
bool
ProcessCoordinator::IsRunning()
{
AutoLocker<BLocker> locker(&fLock);
for (int32 i = 0; i < fNodes.CountItems(); i++) {
if (_IsRunning(fNodes.ItemAt(i)))
return true;
}
return false;
}
void
ProcessCoordinator::Start()
{
_CoordinateAndCallListener();
}
void
ProcessCoordinator::Stop()
{
AutoLocker<BLocker> locker(&fLock);
if (!fWasStopped) {
fWasStopped = true;
printf("[Coordinator] will stop process coordinator\n");
for (int32 i = 0; i < fNodes.CountItems(); i++) {
ProcessNode* node = fNodes.ItemAt(i);
printf("[%s] stopping process", node->Process()->Name());
if (node->Process()->ErrorStatus() != B_OK)
printf(" (error)\n");
printf("\n");
node->StopProcess();
}
}
}
status_t
ProcessCoordinator::ErrorStatus()
{
AutoLocker<BLocker> locker(&fLock);
for (int32 i = 0; i < fNodes.CountItems(); i++) {
status_t result = fNodes.ItemAt(i)->Process()->ErrorStatus();
if (result != B_OK)
return result;
}
return B_OK;
}
float
ProcessCoordinator::Progress()
{
AutoLocker<BLocker> locker(&fLock);
if (!fWasStopped)
return ((float) _CountNodesCompleted()) / ((float) fNodes.CountItems());
return 0.0f;
}
BString
ProcessCoordinator::_CreateStatusMessage()
{
// work through the nodes and take a description from the first one. If
// there are others present then use a 'plus X others' suffix. Go backwards
// through the processes so that the most recent activity is shown first.
BString firstProcessDescription;
uint32 additionalRunningProcesses = 0;
for (int32 i = fNodes.CountItems() - 1; i >= 0; i--) {
AbstractProcess* process = fNodes.ItemAt(i)->Process();
if (process->ProcessState() == PROCESS_RUNNING) {
if (firstProcessDescription.IsEmpty()) {
firstProcessDescription = process->Description();
} else {
additionalRunningProcesses++;
}
}
}
if (firstProcessDescription.IsEmpty())
return "???";
if (additionalRunningProcesses == 0)
return firstProcessDescription;
static BStringFormat format(B_TRANSLATE(
"%FIRST_PROCESS_DESCRIPTION% +"
"{0, plural, one{# process} other{# processes}}"));
BString result;
format.Format(result, additionalRunningProcesses);
result.ReplaceAll("%FIRST_PROCESS_DESCRIPTION%", firstProcessDescription);
return result;
}
/*! This method assumes that a lock is held on the coordinator. */
ProcessCoordinatorState
ProcessCoordinator::_CreateStatus()
{
return ProcessCoordinatorState(
this, Progress(), _CreateStatusMessage(), IsRunning(), ErrorStatus());
}
void
ProcessCoordinator::_CoordinateAndCallListener()
{
ProcessCoordinatorState state = _Coordinate();
if (fListener != NULL)
fListener->CoordinatorChanged(state);
}
ProcessCoordinatorState
ProcessCoordinator::_Coordinate()
{
if (Logger::IsTraceEnabled())
printf("[Coordinator] will coordinate nodes\n");
AutoLocker<BLocker> locker(&fLock);
_StopSuccessorNodesToErroredOrStoppedNodes();
// go through the nodes and find those that are still to be run and
// for which the preconditions are met to start.
for (int32 i = 0; i < fNodes.CountItems(); i++) {
ProcessNode* node = fNodes.ItemAt(i);
if (node->Process()->ProcessState() == PROCESS_INITIAL) {
if (node->AllPredecessorsComplete())
node->StartProcess();
else {
if (Logger::IsTraceEnabled()) {
printf("[Coordinator] all predecessors not complete -> "
"[%s] not started\n", node->Process()->Name());
}
}
} else {
if (Logger::IsTraceEnabled()) {
printf("[Coordinator] process [%s] running or complete\n",
node->Process()->Name());
}
}
}
return _CreateStatus();
}
/*! This method assumes that a lock is held on the coordinator. */
void
ProcessCoordinator::_StopSuccessorNodesToErroredOrStoppedNodes()
{
for (int32 i = 0; i < fNodes.CountItems(); i++) {
ProcessNode* node = fNodes.ItemAt(i);
AbstractProcess* process = node->Process();
if (process->WasStopped() || process->ErrorStatus() != B_OK)
_StopSuccessorNodes(node);
}
}
/*! This method assumes that a lock is held on the coordinator. */
void
ProcessCoordinator::_StopSuccessorNodes(ProcessNode* predecessorNode)
{
for (int32 i = 0; i < predecessorNode->CountSuccessors(); i++) {
ProcessNode* node = predecessorNode->SuccessorAt(i);
AbstractProcess* process = node->Process();
if (process->ProcessState() == PROCESS_INITIAL) {
if (Logger::IsDebugEnabled()) {
printf("[Coordinator] [%s] (failed) --> [%s] (stopping)\n",
predecessorNode->Process()->Name(), process->Name());
}
node->StopProcess();
_StopSuccessorNodes(node);
}
}
}
bool
ProcessCoordinator::_IsRunning(ProcessNode* node)
{
return node->Process()->ProcessState() != PROCESS_COMPLETE;
}
int32
ProcessCoordinator::_CountNodesCompleted()
{
int32 nodesCompleted = 0;
for (int32 i = 0; i < fNodes.CountItems(); i++) {
AbstractProcess *process = fNodes.ItemAt(i)->Process();
if (process->ProcessState() == PROCESS_COMPLETE)
nodesCompleted++;
}
return nodesCompleted;
}
@@ -0,0 +1,115 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_COORDINATOR_H
#define PROCESS_COORDINATOR_H
#include "ProcessCoordinator.h"
#include "AbstractProcess.h"
#include "ProcessNode.h"
#include "List.h"
class ProcessCoordinator;
/*! This class carries the state of the current process coordinator so that
it can be dealt with atomically without having call back to the coordinator.
*/
class ProcessCoordinatorState {
public:
ProcessCoordinatorState(
const ProcessCoordinator*
processCoordinator,
float progress, const BString& message,
bool isRunning, status_t errorStatus);
virtual ~ProcessCoordinatorState();
const ProcessCoordinator* Coordinator() const;
float Progress() const;
BString Message() const;
bool IsRunning() const;
status_t ErrorStatus() const;
private:
const ProcessCoordinator* fProcessCoordinator;
float fProgress;
BString fMessage;
bool fIsRunning;
status_t fErrorStatus;
};
/*! Clients are able to subclass from this 'interface' in order to accept
call-backs when a coordinator has exited; either through failure,
stopping or completion.
*/
class ProcessCoordinatorListener {
public:
/*! Signals to the listener that the coordinator has changed in some way -
for example, a process has started or stopped or even that the whole
coordinator has finished.
*/
virtual void CoordinatorChanged(
ProcessCoordinatorState&
processCoordinatorState) = 0;
};
/*! It is possible to create a number of ProcessNodes (themselves associated
with AbstractProcess-s) that may have dependencies (predecessors and
successors) and then an instance of this class is able to coordinate the
list of ProcessNode-s so that they are all completed in the correct order.
*/
class ProcessCoordinator : public AbstractProcessListener {
public:
ProcessCoordinator(
ProcessCoordinatorListener* listener);
virtual ~ProcessCoordinator();
void AddNode(ProcessNode* nodes);
void ProcessExited();
// AbstractProcessListener
bool IsRunning();
void Start();
void Stop();
status_t ErrorStatus();
float Progress();
private:
bool _IsRunning(ProcessNode* node);
void _CoordinateAndCallListener();
ProcessCoordinatorState
_Coordinate();
ProcessCoordinatorState
_CreateStatus();
BString _CreateStatusMessage();
int32 _CountNodesCompleted();
void _StopSuccessorNodesToErroredOrStoppedNodes();
void _StopSuccessorNodes(ProcessNode* node);
BLocker fLock;
List<ProcessNode*, true>
fNodes;
ProcessCoordinatorListener*
fListener;
bool fWasStopped;
};
#endif // PROCESS_COORDINATOR_H
@@ -0,0 +1,111 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ProcessCoordinatorFactory.h"
#include <Autolock.h>
#include <package/Context.h>
#include <package/PackageRoster.h>
#include "AbstractServerProcess.h"
#include "LocalPkgDataLoadProcess.h"
#include "LocalRepositoryUpdateProcess.h"
#include "Model.h"
#include "PackageInfoListener.h"
#include "ProcessCoordinator.h"
#include "ProcessNode.h"
#include "ServerHelper.h"
#include "ServerIconExportUpdateProcess.h"
#include "ServerPkgDataUpdateProcess.h"
#include "ServerRepositoryDataUpdateProcess.h"
#include "ServerSettings.h"
using namespace BPackageKit;
/* static */ ProcessCoordinator*
ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
PackageInfoListener *packageInfoListener,
ProcessCoordinatorListener* processCoordinatorListener,
Model* model, bool forceLocalUpdate)
{
uint32 serverProcessOptions = _CalculateServerProcessOptions();
BAutolock locker(model->Lock());
ProcessCoordinator* processCoordinator = new ProcessCoordinator(
processCoordinatorListener);
ProcessNode *localRepositoryUpdate =
new ProcessNode(new LocalRepositoryUpdateProcess(model,
forceLocalUpdate));
processCoordinator->AddNode(localRepositoryUpdate);
ProcessNode *localPkgDataLoad =
new ProcessNode(new LocalPkgDataLoadProcess(
packageInfoListener, model, forceLocalUpdate));
localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
processCoordinator->AddNode(localPkgDataLoad);
ProcessNode *serverIconExportUpdate =
new ProcessNode(new ServerIconExportUpdateProcess(model,
serverProcessOptions));
serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverIconExportUpdate);
ProcessNode *serverRepositoryDataUpdate =
new ProcessNode(new ServerRepositoryDataUpdateProcess(model,
serverProcessOptions));
serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverRepositoryDataUpdate);
// create a process for each of the repositories that are configured on the
// local system. Later, only those that have a web-app repository server
// code will be actually processed, but this means that the creation of the
// 'processes' does not need to be dynamic as the process coordinator runs.
BPackageRoster roster;
BStringList repoNames;
status_t repoNamesResult = roster.GetRepositoryNames(repoNames);
if (repoNamesResult == B_OK) {
for (int32 i = 0; i < repoNames.CountStrings(); i++) {
ProcessNode* processNode = new ProcessNode(
new ServerPkgDataUpdateProcess(model->PreferredLanguage(),
repoNames.StringAt(i), model, serverProcessOptions));
processNode->AddPredecessor(serverRepositoryDataUpdate);
processCoordinator->AddNode(processNode);
}
} else {
printf("a problem has arisen getting the repository names.\n");
}
return processCoordinator;
}
/* static */ uint32
ProcessCoordinatorFactory::_CalculateServerProcessOptions()
{
uint32 processOptions = 0;
if (ServerSettings::IsClientTooOld()) {
printf("bulk load proceeding without network communications "
"because the client is too old\n");
processOptions |= SERVER_PROCESS_NO_NETWORKING;
}
if (!ServerHelper::IsNetworkAvailable())
processOptions |= SERVER_PROCESS_NO_NETWORKING;
if (ServerSettings::PreferCache())
processOptions |= SERVER_PROCESS_PREFER_CACHE;
if (ServerSettings::DropCache())
processOptions |= SERVER_PROCESS_DROP_CACHE;
return processOptions;
}
@@ -0,0 +1,33 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_COORDINATOR_FACTORY_H
#define PROCESS_COORDINATOR_FACTORY_H
#include <SupportDefs.h>
class Model;
class PackageInfoListener;
class ProcessCoordinator;
class ProcessCoordinatorListener;
/*! This class is able to create ProcessCoordinators that are loaded-up with
Processes that together complete some larger job.
*/
class ProcessCoordinatorFactory {
public:
static ProcessCoordinator* CreateBulkLoadCoordinator(
PackageInfoListener *packageInfoListener,
ProcessCoordinatorListener*
processCoordinatorListener,
Model* model, bool forceLocalUpdate);
private:
static uint32 _CalculateServerProcessOptions();
};
#endif // PROCESS_COORDINATOR_FACTORY_H
+195
View File
@@ -0,0 +1,195 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ProcessNode.h"
#include <unistd.h>
#include "AbstractProcess.h"
#include "Logger.h"
#define SPIN_UNTIL_STARTED_DELAY_MI 250 * 1000
// quarter of a second
#define TIMEOUT_UNTIL_STARTED_SECS 10
#define TIMEOUT_UNTIL_STOPPED_SECS 10
ProcessNode::ProcessNode(AbstractProcess* process)
:
fWorker(B_BAD_THREAD_ID),
fProcess(process)
{
}
ProcessNode::~ProcessNode()
{
if (fProcess != NULL)
delete fProcess;
}
AbstractProcess*
ProcessNode::Process() const
{
return fProcess;
}
/*! This method will spin-lock the thread until the process is in one of the
states defined by the mask.
*/
status_t
ProcessNode::_SpinUntilProcessState(
uint32 desiredStatesMask, uint32 timeoutSeconds)
{
uint32 start = real_time_clock();
while (true) {
if ((Process()->ProcessState() & desiredStatesMask) != 0)
return B_OK;
usleep(SPIN_UNTIL_STARTED_DELAY_MI);
if (real_time_clock() - start > timeoutSeconds) {
printf("[Node<%s>] timeout waiting for process state\n",
Process()->Name());
return B_ERROR;
}
}
}
/*! Considered to be protected from concurrent access by the ProcessCoordinator
*/
status_t
ProcessNode::StartProcess()
{
if (fWorker != B_BAD_THREAD_ID)
return B_BUSY;
if (Logger::IsInfoEnabled())
printf("[Node<%s>] initiating\n", Process()->Name());
fWorker = spawn_thread(&_StartProcess, Process()->Name(),
B_NORMAL_PRIORITY, Process());
if (fWorker >= 0) {
resume_thread(fWorker);
return _SpinUntilProcessState(PROCESS_RUNNING | PROCESS_COMPLETE,
TIMEOUT_UNTIL_STARTED_SECS);
}
return B_ERROR;
}
/*! Considered to be protected from concurrent access by the ProcessCoordinator
*/
status_t
ProcessNode::StopProcess()
{
Process()->SetListener(NULL);
status_t stopResult = Process()->Stop();
status_t waitResult = _SpinUntilProcessState(PROCESS_COMPLETE,
TIMEOUT_UNTIL_STOPPED_SECS);
// if the thread is still running then it will be necessary to tear it
// down.
if (waitResult != B_OK) {
printf("[%s] process did not stop within timeout - will be stopped "
"uncleanly", Process()->Name());
kill_thread(fWorker);
}
if (stopResult != B_OK)
return stopResult;
if (waitResult != B_OK)
return waitResult;
return B_OK;
}
/*! This method is the initial function that is invoked on starting a new
thread. It will start a process that is part of the bulk-load.
*/
/*static*/ status_t
ProcessNode::_StartProcess(void* cookie)
{
AbstractProcess* process = static_cast<AbstractProcess*>(cookie);
if (Logger::IsInfoEnabled()) {
printf("[Node<%s>] starting process\n", process->Name());
}
process->Run();
return B_OK;
}
void
ProcessNode::AddPredecessor(ProcessNode *node)
{
fPredecessorNodes.Add(node);
node->_AddSuccessor(this);
}
int32
ProcessNode::CountPredecessors() const
{
return fPredecessorNodes.CountItems();
}
ProcessNode*
ProcessNode::PredecessorAt(int32 index) const
{
return fPredecessorNodes.ItemAt(index);
}
bool
ProcessNode::AllPredecessorsComplete() const
{
for (int32 i = 0; i < CountPredecessors(); i++) {
if (PredecessorAt(i)->Process()->ProcessState() != PROCESS_COMPLETE)
return false;
}
return true;
}
void
ProcessNode::_AddSuccessor(ProcessNode* node)
{
fSuccessorNodes.Add(node);
}
int32
ProcessNode::CountSuccessors() const
{
return fSuccessorNodes.CountItems();
}
ProcessNode*
ProcessNode::SuccessorAt(int32 index) const
{
return fSuccessorNodes.ItemAt(index);
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright 2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_NODE_H
#define PROCESS_NODE_H
#include <OS.h>
#include "List.h"
class AbstractProcess;
/*! This class is designed to be used by the ProcessCoordinator class. The
purpose of the class is to hold a process and also any dependent processes
of this one. This effectively creates a dependency tree of processes. This
class is also able to start and stop threads that run the process.
*/
class ProcessNode {
public:
ProcessNode(AbstractProcess* process);
virtual ~ProcessNode();
AbstractProcess* Process() const;
status_t StartProcess();
status_t StopProcess();
void AddPredecessor(ProcessNode* node);
int32 CountPredecessors() const;
ProcessNode* PredecessorAt(int32 index) const;
bool AllPredecessorsComplete() const;
int32 CountSuccessors() const;
ProcessNode* SuccessorAt(int32 index) const;
private:
static status_t _StartProcess(void* cookie);
status_t _SpinUntilProcessState(
uint32 desiredStatesMask,
uint32 timeoutSeconds);
void _AddSuccessor(ProcessNode* node);
thread_id fWorker;
AbstractProcess* fProcess;
List<ProcessNode*, true>
fPredecessorNodes;
List<ProcessNode*, true>
fSuccessorNodes;
};
#endif // PROCESS_TREE_NODE_H
+10 -8
View File
@@ -3,6 +3,7 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerHelper.h"
#include <stdio.h>
@@ -21,6 +22,7 @@
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerHelper"
#define KEY_MSG_MINIMUM_VERSION "minimumVersion"
#define KEY_HEADER_MINIMUM_VERSION "X-Desktop-Application-Minimum-Version"
@@ -31,7 +33,7 @@
to the looper and then onto the user to see.
*/
void
/*static*/ void
ServerHelper::NotifyServerJsonRpcError(BMessage& error)
{
BMessage message(MSG_SERVER_ERROR);
@@ -40,7 +42,7 @@ ServerHelper::NotifyServerJsonRpcError(BMessage& error)
}
void
/*static*/ void
ServerHelper::AlertServerJsonRpcError(BMessage* message)
{
BMessage error;
@@ -84,7 +86,7 @@ ServerHelper::AlertServerJsonRpcError(BMessage* message)
}
void
/*static*/ void
ServerHelper::NotifyTransportError(status_t error)
{
switch (error) {
@@ -105,7 +107,7 @@ ServerHelper::NotifyTransportError(status_t error)
}
void
/*static*/ void
ServerHelper::AlertTransportError(BMessage* message)
{
status_t errno = B_OK;
@@ -139,7 +141,7 @@ ServerHelper::AlertTransportError(BMessage* message)
}
void
/*static*/ void
ServerHelper::NotifyClientTooOld(const BHttpHeaders& responseHeaders)
{
if (!ServerSettings::IsClientTooOld()) {
@@ -157,7 +159,7 @@ ServerHelper::NotifyClientTooOld(const BHttpHeaders& responseHeaders)
}
void
/*static*/ void
ServerHelper::AlertClientTooOld(BMessage* message)
{
BString minimumVersion;
@@ -182,14 +184,14 @@ ServerHelper::AlertClientTooOld(BMessage* message)
}
bool
/*static*/ bool
ServerHelper::IsNetworkAvailable()
{
return !ServerSettings::ForceNoNetwork() && IsPlatformNetworkAvailable();
}
bool
/*static*/ bool
ServerHelper::IsPlatformNetworkAvailable()
{
BNetworkRoster& roster = BNetworkRoster::Default();
@@ -2,7 +2,6 @@
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SERVER_HELPER_H
#define SERVER_HELPER_H
@@ -3,75 +3,95 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerIconExportUpdateProcess.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <Autolock.h>
#include <AutoLocker.h>
#include <Catalog.h>
#include <FileIO.h>
#include <support/StopWatch.h>
#include <support/ZlibCompressionAlgorithm.h>
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "ServerHelper.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
#include "TarArchiveService.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess"
/*! This constructor will locate the cached data in a standardized location */
ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(
AbstractServerProcessListener* listener,
const BPath& localStorageDirectoryPath,
Model* model,
uint32 options)
uint32 serverProcessOptions)
:
AbstractServerProcess(listener, options),
fLocalStorageDirectoryPath(localStorageDirectoryPath),
AbstractServerProcess(serverProcessOptions),
fModel(model),
fLocalIconStore(LocalIconStore(localStorageDirectoryPath)),
fCountIconsSet(0)
{
if (fModel->IconStoragePath(fLocalIconStoragePath) != B_OK) {
printf("[%s] unable to obtain the path for storing icons\n", Name());
fLocalIconStoragePath.Unset();
fLocalIconStore = NULL;
} else {
fLocalIconStore = new LocalIconStore(fLocalIconStoragePath);
}
}
ServerIconExportUpdateProcess::~ServerIconExportUpdateProcess()
{
delete fLocalIconStore;
}
const char*
ServerIconExportUpdateProcess::Name()
ServerIconExportUpdateProcess::Name() const
{
return "ServerIconExportUpdateProcess";
}
const char*
ServerIconExportUpdateProcess::Description() const
{
return B_TRANSLATE("Synchronizing icons");
}
status_t
ServerIconExportUpdateProcess::RunInternal()
{
status_t result = B_OK;
if (NULL == fLocalIconStore || fLocalIconStoragePath.Path() == NULL)
result = B_ERROR;
if (IsSuccess(result) && HasOption(SERVER_PROCESS_DROP_CACHE)) {
result = StorageUtils::RemoveDirectoryContents(
fLocalStorageDirectoryPath);
result = StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath);
}
if (IsSuccess(result)) {
if (result == B_OK) {
bool hasData;
result = HasLocalData(&hasData);
if (IsSuccess(result) && ShouldAttemptNetworkDownload(hasData))
result = DownloadAndUnpack();
if (result == B_OK && ShouldAttemptNetworkDownload(hasData))
result = _DownloadAndUnpack();
if (IsSuccess(result)) {
status_t hasDataResult = HasLocalData(&hasData);
if (IsSuccess(hasDataResult) && !hasData)
if (hasDataResult == B_OK && !hasData)
result = HD_ERR_NO_DATA;
}
}
@@ -83,23 +103,81 @@ ServerIconExportUpdateProcess::RunInternal()
}
status_t
ServerIconExportUpdateProcess::Populate()
{
BStopWatch watch("ServerIconExportUpdateProcess::Populate", true);
DepotList depots = fModel->Depots();
status_t result = B_OK;
{
AutoLocker<BLocker> locker(fModel->Lock());
depots = fModel->Depots();
}
if (Logger::IsDebugEnabled()) {
printf("[%s] will populate icons for %" B_PRId32 " depots\n", Name(),
depots.CountItems());
}
for (int32 i = 0;
(i < depots.CountItems()) && !WasStopped() && (result == B_OK);
i++) {
AutoLocker<BLocker> locker(fModel->Lock());
DepotInfo depotInfo = depots.ItemAtFast(i);
result = PopulateForDepot(depotInfo);
}
if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0;
printf("[%s] did populate %" B_PRId32 " packages' icons (%6.3g secs)\n",
Name(), fCountIconsSet, secs);
}
return result;
}
/*! This method assumes that the model lock has been acquired */
status_t
ServerIconExportUpdateProcess::PopulateForDepot(const DepotInfo& depot)
{
printf("[%s] will populate icons for depot [%s]\n",
Name(), depot.Name().String());
status_t result = B_OK;
PackageList packages = depot.Packages();
for(int32 j = 0;
(j < packages.CountItems()) && !WasStopped() && (result == B_OK);
j++) {
const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j);
result = PopulateForPkg(packageInfoRef);
if (result == B_FILE_NOT_FOUND)
result = B_OK;
}
return result;
}
/*! This method assumes that the model lock has been acquired */
status_t
ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package)
{
BPath bestIconPath;
if ( fLocalIconStore.TryFindIconPath(
if ( fLocalIconStore->TryFindIconPath(
package->Name(), bestIconPath) == B_OK) {
BFile bestIconFile(bestIconPath.Path(), O_RDONLY);
BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true);
// TODO; somehow handle the locking!
//BAutolock locker(&fLock);
package->SetIcon(bitmapRef);
if (Logger::IsDebugEnabled()) {
fprintf(stdout, "have set the package icon for [%s] from [%s]\n",
package->Name().String(), bestIconPath.Path());
printf("[%s] have set the package icon for [%s] from [%s]\n",
Name(), package->Name().String(), bestIconPath.Path());
}
fCountIconsSet++;
@@ -108,52 +186,74 @@ ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package)
}
if (Logger::IsDebugEnabled()) {
fprintf(stdout, "did not set the package icon for [%s]; no data\n",
package->Name().String());
printf("[%s] did not set the package icon for [%s]; no data\n",
Name(), package->Name().String());
}
return B_FILE_NOT_FOUND;
}
bool
ServerIconExportUpdateProcess::ConsumePackage(
const PackageInfoRef& packageInfoRef, void* context)
{
PopulateForPkg(packageInfoRef);
return !WasStopped();
}
status_t
ServerIconExportUpdateProcess::Populate()
{
BStopWatch watch("ServerIconExportUpdateProcess::Populate", true);
fModel->ForAllPackages(this, NULL);
if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0;
fprintf(stdout, "did populate %" B_PRId32 " packages' icons"
" (%6.3g secs)\n", fCountIconsSet, secs);
}
return B_OK;
}
status_t
ServerIconExportUpdateProcess::DownloadAndUnpack()
ServerIconExportUpdateProcess::_DownloadAndUnpack()
{
BPath tarGzFilePath(tmpnam(NULL));
status_t result = B_OK;
printf("will start fetching icons\n");
printf("[%s] will start fetching icons\n", Name());
result = Download(tarGzFilePath);
result = _Download(tarGzFilePath);
switch (result) {
case HD_ERR_NOT_MODIFIED:
printf("[%s] icons not modified - will use existing\n", Name());
return result;
break;
case B_OK:
return _Unpack(tarGzFilePath);
default:
return (_HandleDownloadFailure() != B_OK) ? result : B_OK;
}
}
/*! if the download failed, but there are existing icons in place to use
then use those icons. To detect the existing files, look for the
icons' meta-info file.
*/
status_t
ServerIconExportUpdateProcess::_HandleDownloadFailure()
{
bool hasData;
status_t result = HasLocalData(&hasData);
if (result == B_OK) {
printf("delete any existing stored data\n");
StorageUtils::RemoveDirectoryContents(fLocalStorageDirectoryPath);
if (hasData) {
printf("[%s] failed to update data, but have old data anyway "
"so will carry on with that\n", Name());
} else {
printf("[%s] failed to obtain data\n", Name());
result = HD_ERR_NO_DATA;
}
} else {
printf("[%s] unable to detect if there is local data\n", Name());
}
return result;
}
/*! The tar-ball data of icons has arrived and so old data needs to be purged
to make way for the new data and the new data needs to be unpacked.
*/
status_t
ServerIconExportUpdateProcess::_Unpack(BPath& tarGzFilePath)
{
status_t result;
printf("[%s] delete any existing stored data\n", Name());
StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath);
BFile *tarGzFile = new BFile(tarGzFilePath.Path(), O_RDONLY);
BDataIO* tarIn;
@@ -166,27 +266,27 @@ ServerIconExportUpdateProcess::DownloadAndUnpack()
zlibDecompressionParameters, tarIn);
if (result == B_OK) {
BStopWatch watch("ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack", true);
BStopWatch watch(
"ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack",
true);
result = TarArchiveService::Unpack(*tarIn,
fLocalStorageDirectoryPath, NULL);
fLocalIconStoragePath, NULL);
if (result == B_OK) {
double secs = watch.ElapsedTime() / 1000000.0;
fprintf(stdout, "did unpack icon tgz in (%6.3g secs)\n", secs);
printf("[%s] did unpack icon tgz in (%6.3g secs)\n", Name(),
secs);
if (0 != remove(tarGzFilePath.Path())) {
fprintf(stdout, "unable to delete the temporary tgz path; "
"%s\n", tarGzFilePath.Path());
printf("unable to delete the temporary tgz path; %s\n",
tarGzFilePath.Path());
}
}
}
delete tarGzFile;
printf("did complete fetching icons\n");
}
printf("[%s] did complete unpacking icons\n", Name());
return result;
}
@@ -195,18 +295,32 @@ status_t
ServerIconExportUpdateProcess::HasLocalData(bool* result) const
{
BPath path;
status_t status = GetStandardMetaDataPath(path);
if (status != B_OK)
return status;
off_t size;
GetStandardMetaDataPath(path);
return StorageUtils::ExistsObject(path, result, NULL, &size)
&& size > 0;
status = StorageUtils::ExistsObject(path, result, NULL, &size);
if (status == B_OK && size == 0)
*result = false;
return status;
}
void
status_t
ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
{
path.SetTo(fLocalStorageDirectoryPath.Path());
status_t result = fModel->IconStoragePath(path);
if (result != B_OK)
return result;
path.Append("hicn/info.json");
return B_OK;
}
@@ -220,7 +334,7 @@ ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(
status_t
ServerIconExportUpdateProcess::Download(BPath& tarGzFilePath)
ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath)
{
return DownloadToLocalFileAtomically(tarGzFilePath,
ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz"));
@@ -1,52 +1,53 @@
/*
* Copyright 2017, Andrew Lindesay <[email protected]>.
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H
#define SERVER_ICON_EXPORT_UPDATE_PROCESS_H
#include "AbstractServerProcess.h"
#include "LocalIconStore.h"
#include "Model.h"
#include <File.h>
#include <Path.h>
#include <String.h>
#include <Url.h>
#include "AbstractServerProcess.h"
#include "LocalIconStore.h"
#include "Model.h"
class ServerIconExportUpdateProcess :
public AbstractServerProcess, public PackageConsumer {
class DumpExportPkg;
class ServerIconExportUpdateProcess : public AbstractServerProcess {
public:
ServerIconExportUpdateProcess(
AbstractServerProcessListener* listener,
const BPath& localStorageDirectoryPath,
Model* model, uint32 options);
Model* model, uint32 serverProcessOptions);
virtual ~ServerIconExportUpdateProcess();
const char* Name();
const char* Name() const;
const char* Description() const;
status_t RunInternal();
virtual bool ConsumePackage(
const PackageInfoRef& packageInfoRef,
void *context);
protected:
status_t PopulateForPkg(const PackageInfoRef& package);
status_t PopulateForDepot(const DepotInfo& depot);
status_t Populate();
status_t DownloadAndUnpack();
status_t HasLocalData(bool* result) const;
void GetStandardMetaDataPath(BPath& path) const;
status_t GetStandardMetaDataPath(BPath& path) const;
void GetStandardMetaDataJsonPath(
BString& jsonPath) const;
private:
status_t Download(BPath& tarGzFilePath);
status_t _Unpack(BPath& tarGzFilePath);
status_t _HandleDownloadFailure();
status_t _DownloadAndUnpack();
status_t _Download(BPath& tarGzFilePath);
BPath fLocalStorageDirectoryPath;
Model* fModel;
LocalIconStore fLocalIconStore;
BPath fLocalIconStoragePath;
LocalIconStore* fLocalIconStore;
int32 fCountIconsSet;
};
@@ -3,13 +3,16 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PkgDataUpdateProcess.h"
#include "ServerPkgDataUpdateProcess.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <Autolock.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Catalog.h>
#include <FileIO.h>
#include <support/StopWatch.h>
#include <Url.h>
@@ -25,19 +28,22 @@
#include "HaikuDepotConstants.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerPkgDataUpdateProcess"
/*! This package listener (not at the JSON level) is feeding in the
packages as they are parsed and processing them.
*/
class PackageFillingPkgListener :
public DumpExportPkgListener, public PackageConsumer {
class PackageFillingPkgListener : public DumpExportPkgListener {
public:
PackageFillingPkgListener(Model *model,
BString& depotName, Stoppable* stoppable);
virtual ~PackageFillingPkgListener();
virtual bool ConsumePackage(const PackageInfoRef& package,
void *context);
DumpExportPkg* pkg);
virtual bool Handle(DumpExportPkg* item);
virtual void Complete();
@@ -99,9 +105,8 @@ PackageFillingPkgListener::IndexOfCategoryByName(
bool
PackageFillingPkgListener::ConsumePackage(const PackageInfoRef& package,
void *context)
DumpExportPkg* pkg)
{
DumpExportPkg* pkg = static_cast<DumpExportPkg*>(context);
int32 i;
// Collects all of the changes here into one set of notifications to
@@ -195,7 +200,28 @@ PackageFillingPkgListener::Count()
bool
PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
{
fModel->ForPackageByNameInDepot(fDepotName, *(pkg->Name()), this, pkg);
const DepotInfo* depotInfo = fModel->DepotForName(fDepotName);
if (depotInfo != NULL) {
BString packageName = *(pkg->Name());
int32 packageIndex = depotInfo->PackageIndexByName(packageName);
if (-1 != packageIndex) {
PackageList packages = depotInfo->Packages();
const PackageInfoRef& packageInfoRef =
packages.ItemAtFast(packageIndex);
AutoLocker<BLocker> locker(fModel->Lock());
ConsumePackage(packageInfoRef, pkg);
} else {
printf("[PackageFillingPkgListener] unable to find the pkg [%s]\n",
packageName.String());
}
} else {
printf("[PackageFillingPkgListener] unable to find the depot [%s]\n",
fDepotName.String());
}
return !fStoppable->WasStopped();
}
@@ -206,92 +232,143 @@ PackageFillingPkgListener::Complete()
}
PkgDataUpdateProcess::PkgDataUpdateProcess(
AbstractServerProcessListener* listener,
const BPath& localFilePath,
ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(
BString naturalLanguageCode,
BString repositorySourceCode,
BString depotName,
Model *model,
uint32 options)
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(listener, options),
fLocalFilePath(localFilePath),
AbstractSingleFileServerProcess(serverProcessOptions),
fNaturalLanguageCode(naturalLanguageCode),
fRepositorySourceCode(repositorySourceCode),
fModel(model),
fDepotName(depotName)
{
fName.SetToFormat("PkgDataUpdateProcess<%s>", depotName.String());
fName.SetToFormat("ServerPkgDataUpdateProcess<%s>", depotName.String());
fDescription.SetTo(
B_TRANSLATE("Synchronizing package data for repository "
"'%REPO_NAME%'"));
fDescription.ReplaceAll("%REPO_NAME%", depotName.String());
}
PkgDataUpdateProcess::~PkgDataUpdateProcess()
ServerPkgDataUpdateProcess::~ServerPkgDataUpdateProcess()
{
}
const char*
PkgDataUpdateProcess::Name()
ServerPkgDataUpdateProcess::Name() const
{
return fName.String();
}
const char*
ServerPkgDataUpdateProcess::Description() const
{
return fDescription.String();
}
BString
PkgDataUpdateProcess::UrlPathComponent()
ServerPkgDataUpdateProcess::UrlPathComponent()
{
BString urlPath;
urlPath.SetToFormat("/__pkg/all-%s-%s.json.gz",
fRepositorySourceCode.String(),
_DeriveWebAppRepositorySourceCode().String(),
fNaturalLanguageCode.String());
return urlPath;
}
BPath&
PkgDataUpdateProcess::LocalPath()
status_t
ServerPkgDataUpdateProcess::GetLocalPath(BPath& path) const
{
return fLocalFilePath;
BString webAppRepositorySourceCode = _DeriveWebAppRepositorySourceCode();
if (!webAppRepositorySourceCode.IsEmpty()) {
return fModel->DumpExportPkgDataPath(path, webAppRepositorySourceCode);
}
return B_ERROR;
}
status_t
PkgDataUpdateProcess::ProcessLocalData()
ServerPkgDataUpdateProcess::ProcessLocalData()
{
BStopWatch watch("PkgDataUpdateProcess::ProcessLocalData", true);
BStopWatch watch("ServerPkgDataUpdateProcess::ProcessLocalData", true);
PackageFillingPkgListener* itemListener =
new PackageFillingPkgListener(fModel, fDepotName, this);
ObjectDeleter<PackageFillingPkgListener>
itemListenerDeleter(itemListener);
BulkContainerDumpExportPkgJsonListener* listener =
new BulkContainerDumpExportPkgJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportPkgJsonListener>
listenerDeleter(listener);
status_t result = ParseJsonFromFileWithListener(listener, fLocalFilePath);
BPath localPath;
status_t result = GetLocalPath(localPath);
if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0;
fprintf(stdout, "[%s] did process %" B_PRIi32 " packages' data "
"in (%6.3g secs)\n", Name(), itemListener->Count(), secs);
}
if (result != B_OK)
return result;
result = ParseJsonFromFileWithListener(listener, localPath);
if (B_OK != result)
return result;
if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0;
printf("[%s] did process %" B_PRIi32 " packages' data "
"in (%6.3g secs)\n", Name(), itemListener->Count(), secs);
}
return listener->ErrorStatus();
}
void
PkgDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
status_t
ServerPkgDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
{
path.SetTo(fLocalFilePath.Path());
return GetLocalPath(path);
}
void
PkgDataUpdateProcess::GetStandardMetaDataJsonPath(
ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
{
jsonPath.SetTo("$.info");
}
BString
ServerPkgDataUpdateProcess::_DeriveWebAppRepositorySourceCode() const
{
const DepotInfo* depot = fModel->DepotForName(fDepotName);
if (depot == NULL) {
return BString();
}
return depot->WebAppRepositorySourceCode();
}
status_t
ServerPkgDataUpdateProcess::RunInternal()
{
if (_DeriveWebAppRepositorySourceCode().IsEmpty()) {
if (Logger::IsInfoEnabled()) {
printf("[%s] am not updating data for depot [%s] as there is no"
" web app repository source code available\n",
Name(), fDepotName.String());
}
return B_OK;
}
return AbstractSingleFileServerProcess::RunInternal();
}
@@ -1,54 +1,53 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_DATA_UPDATE_PROCESS_H
#define PACKAGE_DATA_UPDATE_PROCESS_H
#include "AbstractSingleFileServerProcess.h"
#include "Model.h"
#include "PackageInfo.h"
#include <File.h>
#include <Path.h>
#include <String.h>
#include <Url.h>
#include "Model.h"
#include "PackageInfo.h"
class PkgDataUpdateProcess : public AbstractSingleFileServerProcess {
class ServerPkgDataUpdateProcess : public AbstractSingleFileServerProcess {
public:
PkgDataUpdateProcess(
AbstractServerProcessListener* listener,
const BPath& localFilePath,
ServerPkgDataUpdateProcess(
BString naturalLanguageCode,
BString repositorySourceCode,
BString depotName,
Model *model,
uint32 options);
virtual ~PkgDataUpdateProcess();
uint32 serverProcessOptions);
virtual ~ServerPkgDataUpdateProcess();
const char* Name();
const char* Name() const;
const char* Description() const;
protected:
void GetStandardMetaDataPath(BPath& path) const;
virtual status_t RunInternal();
status_t GetStandardMetaDataPath(BPath& path) const;
void GetStandardMetaDataJsonPath(
BString& jsonPath) const;
BString UrlPathComponent();
status_t ProcessLocalData();
BPath& LocalPath();
status_t GetLocalPath(BPath& path) const;
private:
BString _DeriveWebAppRepositorySourceCode() const;
BPath fLocalFilePath;
BString fNaturalLanguageCode;
BString fRepositorySourceCode;
Model* fModel;
BString fDepotName;
BString fName;
BString fDescription;
};
@@ -3,12 +3,15 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "RepositoryDataUpdateProcess.h"
#include "ServerRepositoryDataUpdateProcess.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <AutoDeleter.h>
#include <Catalog.h>
#include <FileIO.h>
#include <Url.h>
@@ -20,6 +23,10 @@
#include "DumpExportRepositoryJsonListener.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerRepositoryDataUpdateProcess"
/*! This repository listener (not at the JSON level) is feeding in the
repositories as they are parsed and processing them. Processing
includes finding the matching depot record and coupling the data
@@ -91,14 +98,17 @@ DepotMatchingRepositoryListener::MapDepot(const DepotInfo& depot, void *context)
BString(*repositorySourceCode));
if (Logger::IsDebugEnabled()) {
printf("associated dept [%s] (%s) with server repository "
"source [%s] (%s)\n", modifiedDepotInfo.Name().String(),
modifiedDepotInfo.BaseURL().String(),
printf("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
"with server repository source [%s] (%s)\n",
modifiedDepotInfo.Name().String(),
modifiedDepotInfo.URL().String(),
repositorySourceCode->String(),
repositoryAndRepositorySource->repositorySource->Url()->String());
} else {
printf("associated depot [%s] with server repository source [%s]\n",
modifiedDepotInfo.Name().String(), repositorySourceCode->String());
printf("[DepotMatchingRepositoryListener] associated depot [%s] with "
"server repository source [%s]\n",
modifiedDepotInfo.Name().String(),
repositorySourceCode->String());
}
return modifiedDepotInfo;
@@ -116,20 +126,11 @@ DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
repositoryAndRepositorySource.repositorySource =
repository->RepositorySourcesItemAt(i);
BString* baseURL = repositoryAndRepositorySource
.repositorySource->Url();
BString* URL = repositoryAndRepositorySource
BString* repoInfoURL = repositoryAndRepositorySource
.repositorySource->RepoInfoUrl();
// to be simplified soon because there will no longer be a need to
// check for the baseURL.
if ((baseURL != NULL && !baseURL->IsEmpty())
|| (URL != NULL && !URL->IsEmpty())) {
fModel->ReplaceDepotByUrl(
URL == NULL ? BString() : *URL,
baseURL == NULL ? BString() : *baseURL,
this,
if (!repoInfoURL->IsEmpty()) {
fModel->ReplaceDepotByUrl(*repoInfoURL, this,
&repositoryAndRepositorySource);
}
}
@@ -144,72 +145,86 @@ DepotMatchingRepositoryListener::Complete()
}
RepositoryDataUpdateProcess::RepositoryDataUpdateProcess(
AbstractServerProcessListener* listener,
const BPath& localFilePath,
ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess(
Model* model,
uint32 options)
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(listener, options),
fLocalFilePath(localFilePath),
AbstractSingleFileServerProcess(serverProcessOptions),
fModel(model)
{
}
RepositoryDataUpdateProcess::~RepositoryDataUpdateProcess()
ServerRepositoryDataUpdateProcess::~ServerRepositoryDataUpdateProcess()
{
}
const char*
RepositoryDataUpdateProcess::Name()
ServerRepositoryDataUpdateProcess::Name() const
{
return "RepositoryDataUpdateProcess";
return "ServerRepositoryDataUpdateProcess";
}
const char*
ServerRepositoryDataUpdateProcess::Description() const
{
return B_TRANSLATE("Synchronizing meta-data about repositories");
}
BString
RepositoryDataUpdateProcess::UrlPathComponent()
ServerRepositoryDataUpdateProcess::UrlPathComponent()
{
return BString("/__repository/all-en.json.gz");
}
BPath&
RepositoryDataUpdateProcess::LocalPath()
status_t
ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
{
return fLocalFilePath;
return fModel->DumpExportRepositoryDataPath(path);
}
status_t
RepositoryDataUpdateProcess::ProcessLocalData()
ServerRepositoryDataUpdateProcess::ProcessLocalData()
{
DepotMatchingRepositoryListener* itemListener =
new DepotMatchingRepositoryListener(fModel, this);
ObjectDeleter<DepotMatchingRepositoryListener>
itemListenerDeleter(itemListener);
BulkContainerDumpExportRepositoryJsonListener* listener =
new BulkContainerDumpExportRepositoryJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener>
listenerDeleter(listener);
status_t result = ParseJsonFromFileWithListener(listener, fLocalFilePath);
BPath localPath;
status_t result = GetLocalPath(localPath);
if (B_OK != result)
if (result != B_OK)
return result;
result = ParseJsonFromFileWithListener(listener, localPath);
if (result != B_OK)
return result;
return listener->ErrorStatus();
}
void
RepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
status_t
ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
{
path.SetTo(fLocalFilePath.Path());
return GetLocalPath(path);
}
void
RepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(
ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
{
jsonPath.SetTo("$.info");
@@ -1,45 +1,46 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef REPOSITORY_DATA_UPDATE_PROCESS_H
#define REPOSITORY_DATA_UPDATE_PROCESS_H
#include "AbstractSingleFileServerProcess.h"
#include "Model.h"
#include "PackageInfo.h"
#include <File.h>
#include <Path.h>
#include <String.h>
#include <Url.h>
#include "Model.h"
#include "PackageInfo.h"
class RepositoryDataUpdateProcess : public AbstractSingleFileServerProcess {
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerRepositoryDataUpdateProcess"
class ServerRepositoryDataUpdateProcess : public AbstractSingleFileServerProcess {
public:
RepositoryDataUpdateProcess(
AbstractServerProcessListener* listener,
const BPath& localFilePath,
Model* model, uint32 options);
virtual ~RepositoryDataUpdateProcess();
ServerRepositoryDataUpdateProcess(
Model* model, uint32 serverProcessOptions);
virtual ~ServerRepositoryDataUpdateProcess();
const char* Name();
const char* Name() const;
const char* Description() const;
protected:
void GetStandardMetaDataPath(BPath& path) const;
status_t GetStandardMetaDataPath(BPath& path) const;
void GetStandardMetaDataJsonPath(
BString& jsonPath) const;
BString UrlPathComponent();
status_t ProcessLocalData();
BPath& LocalPath();
status_t GetLocalPath(BPath& path) const;
private:
BPath fLocalFilePath;
Model* fModel;
};
@@ -3,8 +3,10 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "StandardMetaData.h"
StandardMetaData::StandardMetaData()
{
fCreateTimestamp = 0;
@@ -3,10 +3,12 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "StandardMetaDataJsonEventListener.h"
#include "stdio.h"
#define KEY_CREATE_TIMESTAMP "createTimestamp"
#define KEY_DATA_MODIFIED_TIMESTAMP "dataModifiedTimestamp"
@@ -11,8 +11,10 @@
#include <String.h>
#include <StringList.h>
class SmdStackedEventListener;
class StandardMetaDataJsonEventListener : public BJsonEventListener {
friend class SmdStackedEventListener;
public:
@@ -1,8 +1,9 @@
/*
* Copyright 2017, Andrew Lindesay <[email protected]>.
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "TarArchiveService.h"
#include <stdio.h>
@@ -38,7 +39,8 @@ TarArchiveService::Unpack(BDataIO& tarDataIo, BPath& targetDirectory,
count_items_read++;
if (0 == memcmp(zero_buffer, buffer, sizeof zero_buffer)) {
fprintf(stdout, "detected end of tar-ball\n");
if (Logger::IsDebugEnabled())
printf("detected end of tar-ball\n");
return B_OK; // end of tar-ball.
} else {
TarArchiveHeader* header = TarArchiveHeader::CreateFromBlock(
+1 -3
View File
@@ -1,12 +1,10 @@
/*
* Copyright 2017, Andrew Lindesay <[email protected]>.
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef TAR_ARCHIVE_SERVICE_H
#define TAR_ARCHIVE_SERVICE_H
#include "AbstractServerProcess.h"
#include "Stoppable.h"
#include "TarArchiveHeader.h"
+31 -4
View File
@@ -68,7 +68,7 @@ App::QuitRequested()
_StoreSettings(windowSettings);
}
return true;
return BApplication::QuitRequested();
}
@@ -93,7 +93,7 @@ App::MessageReceived(BMessage* message)
case MSG_MAIN_WINDOW_CLOSED:
{
BMessage windowSettings;
if (message->FindMessage("window settings",
if (message->FindMessage(KEY_WINDOW_SETTINGS,
&windowSettings) == B_OK) {
_StoreSettings(windowSettings);
}
@@ -116,6 +116,10 @@ App::MessageReceived(BMessage* message)
ServerHelper::AlertServerJsonRpcError(message);
break;
case MSG_ALERT_SIMPLE_ERROR:
_AlertSimpleError(message);
break;
case MSG_SERVER_DATA_CHANGED:
fMainWindow->PostMessage(message);
break;
@@ -308,6 +312,29 @@ App::ArgvReceived(int32 argc, char* argv[])
}
/*! This method will display an alert based on a message. This message arrives
from a number of possible background threads / processes in the application.
*/
void
App::_AlertSimpleError(BMessage* message)
{
BString alertTitle;
BString alertText;
if (message->FindString(KEY_ALERT_TEXT, &alertText) != B_OK)
alertText = "?";
if (message->FindString(KEY_ALERT_TITLE, &alertTitle) != B_OK)
alertTitle = B_TRANSLATE("Error");
BAlert* alert = new BAlert(alertTitle, alertText, B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
// #pragma mark - private
@@ -359,7 +386,7 @@ App::_LoadSettings(BMessage& settings)
{
if (!fSettingsRead) {
fSettings = true;
if (load_settings(&fSettings, "main_settings", "HaikuDepot") != B_OK)
if (load_settings(&fSettings, KEY_MAIN_SETTINGS, "HaikuDepot") != B_OK)
fSettings.MakeEmpty();
}
settings = fSettings;
@@ -390,7 +417,7 @@ App::_StoreSettings(const BMessage& settings)
}
}
save_settings(&fSettings, "main_settings", "HaikuDepot");
save_settings(&fSettings, KEY_MAIN_SETTINGS, "HaikuDepot");
}
+2
View File
@@ -1,5 +1,6 @@
/*
* Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2018, Andrew Lindesay <[email protected]>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef APP_H
@@ -25,6 +26,7 @@ public:
virtual void ArgvReceived(int32 argc, char* argv[]);
private:
void _AlertSimpleError(BMessage* message);
void _Open(const BEntry& entry);
void _ShowWindow(MainWindow* window);
+107 -417
View File
@@ -32,30 +32,19 @@
#include <StringView.h>
#include <TabView.h>
#include <package/Context.h>
#include <package/manager/Exceptions.h>
#include <package/manager/RepositoryBuilder.h>
#include <package/RefreshRepositoryRequest.h>
#include <package/PackageRoster.h>
#include "package/RepositoryCache.h"
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverProblem.h>
#include <package/solver/SolverProblemSolution.h>
#include <package/solver/SolverRepository.h>
#include <package/solver/SolverResult.h>
#include "AppUtils.h"
#include "AutoDeleter.h"
#include "AutoLocker.h"
#include "DecisionProvider.h"
#include "FeaturedPackagesView.h"
#include "FilterView.h"
#include "JobStateListener.h"
#include "Logger.h"
#include "PackageInfoView.h"
#include "PackageListView.h"
#include "PackageManager.h"
#include "ProcessCoordinator.h"
#include "ProcessCoordinatorFactory.h"
#include "RatePackageWindow.h"
#include "RepositoryUrlUtils.h"
#include "support.h"
#include "ScreenshotWindow.h"
#include "UserLoginWindow.h"
@@ -67,7 +56,7 @@
enum {
MSG_MODEL_WORKER_DONE = 'mmwd',
MSG_BULK_LOAD_DONE = 'mmwd',
MSG_REFRESH_REPOS = 'mrrp',
MSG_MANAGE_REPOS = 'mmrp',
MSG_SOFTWARE_UPDATER = 'mswu',
@@ -75,6 +64,8 @@ enum {
MSG_LOG_OUT = 'lgot',
MSG_AUTHORIZATION_CHANGED = 'athc',
MSG_PACKAGE_CHANGED = 'pchd',
MSG_WORK_STATUS_CHANGE = 'wsch',
MSG_WORK_STATUS_CLEAR = 'wscl',
MSG_SHOW_FEATURED_PACKAGES = 'sofp',
MSG_SHOW_AVAILABLE_PACKAGES = 'savl',
@@ -133,10 +124,8 @@ MainWindow::MainWindow(const BMessage& settings)
fLogInItem(NULL),
fLogOutItem(NULL),
fModelListener(new MessageModelListener(BMessenger(this)), true),
fBulkLoadStateMachine(&fModel),
fTerminating(false),
fSinglePackageMode(false),
fModelWorker(B_BAD_THREAD_ID)
fBulkLoadProcessCoordinator(NULL),
fSinglePackageMode(false)
{
BMenuBar* menuBar = new BMenuBar("Main Menu");
_BuildMenu(menuBar);
@@ -216,7 +205,7 @@ MainWindow::MainWindow(const BMessage& settings)
BPackageRoster().StartWatching(this,
B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
_StartRefreshWorker();
_StartBulkLoad();
_InitWorkerThreads();
}
@@ -233,10 +222,8 @@ MainWindow::MainWindow(const BMessage& settings, const PackageInfoRef& package)
fLogInItem(NULL),
fLogOutItem(NULL),
fModelListener(new MessageModelListener(BMessenger(this)), true),
fBulkLoadStateMachine(&fModel),
fTerminating(false),
fSinglePackageMode(true),
fModelWorker(B_BAD_THREAD_ID)
fBulkLoadProcessCoordinator(NULL),
fSinglePackageMode(true)
{
fFilterView = new FilterView();
fPackageListView = new PackageListView(fModel.Lock());
@@ -263,10 +250,6 @@ MainWindow::~MainWindow()
{
BPackageRoster().StopWatching(this);
fTerminating = true;
if (fModelWorker >= 0)
wait_for_thread(fModelWorker, NULL);
delete_sem(fPendingActionsSem);
if (fPendingActionsWorker >= 0)
wait_for_thread(fPendingActionsWorker, NULL);
@@ -292,10 +275,12 @@ MainWindow::QuitRequested()
StoreSettings(settings);
BMessage message(MSG_MAIN_WINDOW_CLOSED);
message.AddMessage("window settings", &settings);
message.AddMessage(KEY_WINDOW_SETTINGS, &settings);
be_app->PostMessage(&message);
_StopBulkLoad();
return true;
}
@@ -304,14 +289,9 @@ void
MainWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_MODEL_WORKER_DONE:
{
fModelWorker = B_BAD_THREAD_ID;
_AdoptModel();
_UpdateAvailableRepositories();
fWorkStatusView->SetIdle();
case MSG_BULK_LOAD_DONE:
_BulkLoadCompleteReceived();
break;
}
case B_SIMPLE_DATA:
case B_REFS_RECEIVED:
// TODO: ?
@@ -320,11 +300,15 @@ MainWindow::MessageReceived(BMessage* message)
case B_PACKAGE_UPDATE:
// TODO: We should do a more selective update depending on the
// "event", "location", and "change count" fields!
_StartRefreshWorker(false);
_StartBulkLoad(false);
break;
case MSG_REFRESH_REPOS:
_StartRefreshWorker(true);
_StartBulkLoad(true);
break;
case MSG_WORK_STATUS_CHANGE:
_HandleWorkStatusChangeMessageReceived(message);
break;
case MSG_MANAGE_REPOS:
@@ -685,8 +669,9 @@ void
MainWindow::_BuildMenu(BMenuBar* menuBar)
{
BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh repositories"),
new BMessage(MSG_REFRESH_REPOS)));
fRefreshRepositoriesItem = new BMenuItem(
B_TRANSLATE("Refresh repositories"), new BMessage(MSG_REFRESH_REPOS));
menu->AddItem(fRefreshRepositoriesItem);
menu->AddItem(new BMenuItem(B_TRANSLATE("Manage repositories"
B_UTF8_ELLIPSIS), new BMessage(MSG_MANAGE_REPOS)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Check for updates"
@@ -886,407 +871,123 @@ MainWindow::_ClearPackage()
void
MainWindow::_RefreshRepositories(bool force)
MainWindow::_StopBulkLoad()
{
if (fSinglePackageMode)
return;
AutoLocker<BLocker> lock(&fBulkLoadProcessCoordinatorLock);
BPackageRoster roster;
BStringList repositoryNames;
status_t result = roster.GetRepositoryNames(repositoryNames);
if (result != B_OK)
return;
DecisionProvider decisionProvider;
JobStateListener listener;
BContext context(decisionProvider, listener);
BRepositoryCache cache;
for (int32 i = 0; i < repositoryNames.CountStrings(); ++i) {
const BString& repoName = repositoryNames.StringAt(i);
BRepositoryConfig repoConfig;
result = roster.GetRepositoryConfig(repoName, &repoConfig);
if (result != B_OK) {
// TODO: notify user
continue;
}
if (roster.GetRepositoryCache(repoName, &cache) != B_OK || force) {
try {
BRefreshRepositoryRequest refreshRequest(context, repoConfig);
result = refreshRequest.Process();
} catch (BFatalErrorException ex) {
BString message(B_TRANSLATE("An error occurred while "
"refreshing the repository: %error% (%details%)"));
message.ReplaceFirst("%error%", ex.Message());
message.ReplaceFirst("%details%", ex.Details());
_NotifyUser("Error", message.String());
} catch (BException ex) {
BString message(B_TRANSLATE("An error occurred while "
"refreshing the repository: %error%"));
message.ReplaceFirst("%error%", ex.Message());
_NotifyUser("Error", message.String());
}
}
if (fBulkLoadProcessCoordinator != NULL) {
printf("will stop full update process coordinator\n");
fBulkLoadProcessCoordinator->Stop();
}
}
void
MainWindow::_RefreshPackageList(bool force)
MainWindow::_StartBulkLoad(bool force)
{
if (fSinglePackageMode)
return;
AutoLocker<BLocker> lock(&fBulkLoadProcessCoordinatorLock);
if (Logger::IsDebugEnabled())
printf("will refresh the package list\n");
BPackageRoster roster;
BStringList repositoryNames;
status_t result = roster.GetRepositoryNames(repositoryNames);
if (result != B_OK)
return;
std::vector<DepotInfo> depots(repositoryNames.CountStrings());
for (int32 i = 0; i < repositoryNames.CountStrings(); i++) {
const BString& repoName = repositoryNames.StringAt(i);
DepotInfo depotInfo = DepotInfo(repoName);
BRepositoryConfig repoConfig;
status_t getRepositoryConfigStatus = roster.GetRepositoryConfig(
repoName, &repoConfig);
if (getRepositoryConfigStatus == B_OK) {
depotInfo.SetBaseURL(repoConfig.BaseURL());
depotInfo.SetURL(repoConfig.URL());
if (Logger::IsDebugEnabled()) {
printf("local repository [%s] info;\n"
" * base url [%s]\n"
" * url [%s]\n",
repoName.String(), repoConfig.BaseURL().String(),
repoConfig.URL().String());
}
} else {
printf("unable to obtain the repository config for local "
"repository '%s'; %s\n",
repoName.String(), strerror(getRepositoryConfigStatus));
if (fBulkLoadProcessCoordinator == NULL) {
fBulkLoadProcessCoordinator
= ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
this,
// PackageInfoListener
this,
// ProcessCoordinatorListener
&fModel, force);
fBulkLoadProcessCoordinator->Start();
fRefreshRepositoriesItem->SetEnabled(false);
}
}
depots[i] = depotInfo;
}
PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
try {
manager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
| PackageManager::B_ADD_REMOTE_REPOSITORIES);
} catch (BException ex) {
BString message(B_TRANSLATE("An error occurred while "
"initializing the package manager: %message%"));
message.ReplaceFirst("%message%", ex.Message());
_NotifyUser("Error", message.String());
return;
}
/*! This method is called when there is some change in the bulk load process.
A change may mean that a new process has started / stopped etc... or it
may mean that the entire coordinator has finished.
*/
BObjectList<BSolverPackage> packages;
result = manager.Solver()->FindPackages("",
BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
| BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
| BSolver::B_FIND_IN_PROVIDES,
packages);
if (result != B_OK) {
BString message(B_TRANSLATE("An error occurred while "
"obtaining the package list: %message%"));
message.ReplaceFirst("%message%", strerror(result));
_NotifyUser("Error", message.String());
return;
}
void
MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState)
{
AutoLocker<BLocker> lock(&fBulkLoadProcessCoordinatorLock);
if (packages.IsEmpty())
return;
PackageInfoMap foundPackages;
// if a given package is installed locally, we will potentially
// get back multiple entries, one for each local installation
// location, and one for each remote repository the package
// is available in. The above map is used to ensure that in such
// cases we consolidate the information, rather than displaying
// duplicates
PackageInfoMap remotePackages;
// any package that we find in a remote repository goes in this map.
// this is later used to discern which packages came from a local
// installation only, as those must be handled a bit differently
// upon uninstallation, since we'd no longer be able to pull them
// down remotely.
BStringList systemFlaggedPackages;
// any packages flagged as a system package are added to this list.
// such packages cannot be uninstalled, nor can any of their deps.
PackageInfoMap systemInstalledPackages;
// any packages installed in system are added to this list.
// This is later used for dependency resolution of the actual
// system packages in order to compute the list of protected
// dependencies indicated above.
for (int32 i = 0; i < packages.CountItems(); i++) {
BSolverPackage* package = packages.ItemAt(i);
const BPackageInfo& repoPackageInfo = package->Info();
const BString repositoryName = package->Repository()->Name();
PackageInfoRef modelInfo;
PackageInfoMap::iterator it = foundPackages.find(
repoPackageInfo.Name());
if (it != foundPackages.end())
modelInfo.SetTo(it->second);
if (fBulkLoadProcessCoordinator == coordinatorState.Coordinator()) {
if (!coordinatorState.IsRunning())
_BulkLoadProcessCoordinatorFinished(coordinatorState);
else {
// Add new package info
modelInfo.SetTo(new(std::nothrow) PackageInfo(repoPackageInfo),
true);
if (modelInfo.Get() == NULL)
return;
foundPackages[repoPackageInfo.Name()] = modelInfo;
}
// The package list here considers those packages that are installed
// in the system as well as those that exist in remote repositories.
// It is better if the 'depot name' is from the remote repository
// because then it will be possible to perform a rating on it later.
if (modelInfo->DepotName().IsEmpty()
|| modelInfo->DepotName() == REPOSITORY_NAME_SYSTEM
|| modelInfo->DepotName() == REPOSITORY_NAME_INSTALLED) {
modelInfo->SetDepotName(repositoryName);
}
modelInfo->AddListener(this);
BSolverRepository* repository = package->Repository();
BPackageManager::RemoteRepository* remoteRepository =
dynamic_cast<BPackageManager::RemoteRepository*>(repository);
if (remoteRepository != NULL) {
std::vector<DepotInfo>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (RepositoryUrlUtils::EqualsOnUrlOrBaseUrl(
it->URL(), remoteRepository->Config().URL(),
it->BaseURL(), remoteRepository->Config().BaseURL())) {
break;
}
}
if (it == depots.end()) {
if (Logger::IsDebugEnabled()) {
printf("pkg [%s] repository [%s] not recognized"
" --> ignored\n",
modelInfo->Name().String(), repositoryName.String());
_NotifyWorkStatusChange(coordinatorState.Message(),
coordinatorState.Progress());
// show the progress to the user.
}
} else {
it->AddPackage(modelInfo);
if (Logger::IsTraceEnabled()) {
printf("pkg [%s] assigned to [%s]\n",
modelInfo->Name().String(), repositoryName.String());
if (Logger::IsInfoEnabled()) {
printf("unknown process coordinator changed\n");
}
}
remotePackages[modelInfo->Name()] = modelInfo;
} else {
if (repository == static_cast<const BSolverRepository*>(
manager.SystemRepository())) {
modelInfo->AddInstallationLocation(
B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
if (!modelInfo->IsSystemPackage()) {
systemInstalledPackages[repoPackageInfo.FileName()]
= modelInfo;
}
} else if (repository == static_cast<const BSolverRepository*>(
manager.HomeRepository())) {
modelInfo->AddInstallationLocation(
B_PACKAGE_INSTALLATION_LOCATION_HOME);
}
}
if (modelInfo->IsSystemPackage())
systemFlaggedPackages.Add(repoPackageInfo.FileName());
}
bool wasEmpty = fModel.Depots().IsEmpty();
if (force || wasEmpty)
fBulkLoadStateMachine.Stop();
BAutolock lock(fModel.Lock());
if (force)
fModel.Clear();
// filter remote packages from the found list
// any packages remaining will be locally installed packages
// that weren't acquired from a repository
for (PackageInfoMap::iterator it = remotePackages.begin();
it != remotePackages.end(); it++) {
foundPackages.erase(it->first);
}
if (!foundPackages.empty()) {
BString repoName = B_TRANSLATE("Local");
depots.push_back(DepotInfo(repoName));
for (PackageInfoMap::iterator it = foundPackages.begin();
it != foundPackages.end(); ++it) {
depots.back().AddPackage(it->second);
}
}
{
std::vector<DepotInfo>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (fModel.HasDepot(it->Name()))
fModel.SyncDepot(*it);
else
fModel.AddDepot(*it);
}
}
// start retrieving package icons and average ratings
if (force || wasEmpty) {
fBulkLoadStateMachine.Start();
}
// compute the OS package dependencies
try {
// create the solver
BSolver* solver;
status_t error = BSolver::Create(solver);
if (error != B_OK)
throw BFatalErrorException(error, "Failed to create solver.");
ObjectDeleter<BSolver> solverDeleter(solver);
BPath systemPath;
error = find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &systemPath);
if (error != B_OK) {
throw BFatalErrorException(error,
"Unable to retrieve system packages directory.");
}
// add the "installed" repository with the given packages
BSolverRepository installedRepository;
{
BRepositoryBuilder installedRepositoryBuilder(installedRepository,
REPOSITORY_NAME_INSTALLED);
for (int32 i = 0; i < systemFlaggedPackages.CountStrings(); i++) {
BPath packagePath(systemPath);
packagePath.Append(systemFlaggedPackages.StringAt(i));
installedRepositoryBuilder.AddPackage(packagePath.Path());
}
installedRepositoryBuilder.AddToSolver(solver, true);
}
// add system repository
BSolverRepository systemRepository;
{
BRepositoryBuilder systemRepositoryBuilder(systemRepository,
REPOSITORY_NAME_SYSTEM);
for (PackageInfoMap::iterator it = systemInstalledPackages.begin();
it != systemInstalledPackages.end(); it++) {
BPath packagePath(systemPath);
packagePath.Append(it->first);
systemRepositoryBuilder.AddPackage(packagePath.Path());
}
systemRepositoryBuilder.AddToSolver(solver, false);
}
// solve
error = solver->VerifyInstallation();
if (error != B_OK) {
throw BFatalErrorException(error, "Failed to compute packages to "
"install.");
}
BSolverResult solverResult;
error = solver->GetResult(solverResult);
if (error != B_OK) {
throw BFatalErrorException(error, "Failed to retrieve system "
"package dependency list.");
}
for (int32 i = 0; const BSolverResultElement* element
= solverResult.ElementAt(i); i++) {
BSolverPackage* package = element->Package();
if (element->Type() == BSolverResultElement::B_TYPE_INSTALL) {
PackageInfoMap::iterator it = systemInstalledPackages.find(
package->Info().FileName());
if (it != systemInstalledPackages.end())
it->second->SetSystemDependency(true);
}
}
} catch (BFatalErrorException ex) {
printf("Fatal exception occurred while resolving system dependencies: "
"%s, details: %s\n", strerror(ex.Error()), ex.Details().String());
} catch (BNothingToDoException) {
// do nothing
} catch (BException ex) {
printf("Exception occurred while resolving system dependencies: %s\n",
ex.Message().String());
} catch (...) {
printf("Unknown exception occurred while resolving system "
"dependencies.\n");
}
if (Logger::IsDebugEnabled())
printf("did refresh the package list\n");
}
void
MainWindow::_StartRefreshWorker(bool force)
MainWindow::_BulkLoadProcessCoordinatorFinished(
ProcessCoordinatorState& coordinatorState)
{
if (fModelWorker != B_BAD_THREAD_ID)
return;
RefreshWorkerParameters* parameters = new(std::nothrow)
RefreshWorkerParameters(this, force);
if (parameters == NULL)
return;
fWorkStatusView->SetBusy(B_TRANSLATE("Refreshing" B_UTF8_ELLIPSIS));
ObjectDeleter<RefreshWorkerParameters> deleter(parameters);
fModelWorker = spawn_thread(&_RefreshModelThreadWorker, "model loader",
B_LOW_PRIORITY, parameters);
if (fModelWorker > 0) {
deleter.Detach();
resume_thread(fModelWorker);
if (coordinatorState.ErrorStatus() != B_OK) {
AppUtils::NotifySimpleError(
B_TRANSLATE("Package Update Error"),
B_TRANSLATE("While updating package data, a problem has arisen "
"that may cause data to be outdated or missing from the "
"application's display. Additional details regarding this "
"problem may be able to be obtained from the application "
"logs."));
}
BMessenger messenger(this);
messenger.SendMessage(MSG_BULK_LOAD_DONE);
// it is safe to delete the coordinator here because it is already known
// that all of the processes have completed and their threads will have
// exited safely by this point.
delete fBulkLoadProcessCoordinator;
fBulkLoadProcessCoordinator = NULL;
fRefreshRepositoriesItem->SetEnabled(true);
}
status_t
MainWindow::_RefreshModelThreadWorker(void* arg)
void
MainWindow::_BulkLoadCompleteReceived()
{
RefreshWorkerParameters* parameters
= reinterpret_cast<RefreshWorkerParameters*>(arg);
MainWindow* mainWindow = parameters->window;
ObjectDeleter<RefreshWorkerParameters> deleter(parameters);
_AdoptModel();
_UpdateAvailableRepositories();
fWorkStatusView->SetIdle();
}
BMessenger messenger(mainWindow);
mainWindow->_RefreshRepositories(parameters->forceRefresh);
/*! Sends off a message to the Window so that it can change the status view
on the front-end in the UI thread.
*/
if (mainWindow->fTerminating)
return B_OK;
void
MainWindow::_NotifyWorkStatusChange(const BString& text, float progress)
{
BMessage message(MSG_WORK_STATUS_CHANGE);
mainWindow->_RefreshPackageList(parameters->forceRefresh);
if (!text.IsEmpty())
message.AddString(KEY_WORK_STATUS_TEXT, text);
message.AddFloat(KEY_WORK_STATUS_PROGRESS, progress);
messenger.SendMessage(MSG_MODEL_WORKER_DONE);
this->PostMessage(&message, this);
}
return B_OK;
void
MainWindow::_HandleWorkStatusChangeMessageReceived(const BMessage* message)
{
BString text;
float progress;
if (message->FindString(KEY_WORK_STATUS_TEXT, &text) == B_OK)
fWorkStatusView->SetText(text);
if (message->FindFloat(KEY_WORK_STATUS_PROGRESS, &progress) == B_OK)
fWorkStatusView->SetProgress(progress);
}
@@ -1455,17 +1156,6 @@ MainWindow::_PackagesToShowWorker(void* arg)
}
void
MainWindow::_NotifyUser(const char* title, const char* message)
{
BAlert* alert = new(std::nothrow) BAlert(title, message,
B_TRANSLATE("Close"));
if (alert != NULL)
alert->Go();
}
void
MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage)
{
+21 -13
View File
@@ -11,11 +11,11 @@
#include <Window.h>
#include "TabView.h"
#include "BulkLoadStateMachine.h"
#include "Model.h"
#include "PackageAction.h"
#include "PackageActionHandler.h"
#include "PackageInfoListener.h"
#include "ProcessCoordinator.h"
#include "HaikuDepotConstants.h"
@@ -33,7 +33,7 @@ class WorkStatusView;
class MainWindow : public BWindow, private PackageInfoListener,
private PackageActionHandler {
private PackageActionHandler, public ProcessCoordinatorListener {
public:
MainWindow(const BMessage& settings);
MainWindow(const BMessage& settings,
@@ -46,6 +46,9 @@ public:
void StoreSettings(BMessage& message) const;
// ProcessCoordinatorListener
virtual void CoordinatorChanged(
ProcessCoordinatorState& coordinatorState);
private:
// PackageInfoListener
virtual void PackageChanged(
@@ -58,6 +61,9 @@ private:
virtual Model* GetModel();
private:
void _BulkLoadProcessCoordinatorFinished(
ProcessCoordinatorState&
processCoordinatorState);
bool _SelectedPackageHasWebAppRepositoryCode();
void _BuildMenu(BMenuBar* menuBar);
@@ -73,19 +79,21 @@ private:
void _AdoptPackage(const PackageInfoRef& package);
void _ClearPackage();
void _RefreshRepositories(bool force);
void _RefreshPackageList(bool force);
void _PopulatePackageAsync(bool forcePopulate);
void _StartRefreshWorker(bool force = false);
void _StopBulkLoad();
void _StartBulkLoad(bool force = false);
void _BulkLoadCompleteReceived();
void _NotifyWorkStatusChange(const BString& text,
float progress);
void _HandleWorkStatusChangeMessageReceived(
const BMessage* message);
static status_t _RefreshModelThreadWorker(void* arg);
static status_t _PackageActionWorker(void* arg);
static status_t _PopulatePackageWorker(void* arg);
static status_t _PackagesToShowWorker(void* arg);
void _NotifyUser(const char* title,
const char* message);
void _OpenLoginWindow(
const BMessage& onSuccessMessage);
void _UpdateAuthorization();
@@ -114,15 +122,15 @@ private:
BMenuItem* fShowDevelopPackagesItem;
BMenuItem* fShowSourcePackagesItem;
BMenuItem* fRefreshRepositoriesItem;
Model fModel;
ModelListenerRef fModelListener;
PackageList fVisiblePackages;
BulkLoadStateMachine
fBulkLoadStateMachine;
ProcessCoordinator* fBulkLoadProcessCoordinator;
BLocker fBulkLoadProcessCoordinatorLock;
bool fTerminating;
bool fSinglePackageMode;
thread_id fModelWorker;
thread_id fPendingActionsWorker;
PackageActionList fPendingActions;
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AppUtils.h"
#include <string.h>
#include <Application.h>
#include <Messenger.h>
#include "HaikuDepotConstants.h"
/*! This method can be called to pop up an error in the user interface;
typically in a background thread.
*/
/* static */ void
AppUtils::NotifySimpleError(const char* title, const char* text)
{
BMessage message(MSG_ALERT_SIMPLE_ERROR);
if (title != NULL && strlen(title) != 0)
message.AddString(KEY_ALERT_TITLE, title);
if (text != NULL && strlen(text) != 0)
message.AddString(KEY_ALERT_TEXT, text);
be_app->PostMessage(&message);
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef APP_UTILS_H
#define APP_UTILS_H
class AppUtils {
public:
static void NotifySimpleError(const char* title,
const char* text);
};
#endif // APP_UTILS_H
-2
View File
@@ -2,8 +2,6 @@
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "DataIOUtils.h"
@@ -33,18 +33,3 @@ RepositoryUrlUtils::EqualsNormalized(const BString& url1, const BString& url2)
return normalizedUrl1 == normalizedUrl2;
}
/*! Matches on either the identifier URL of the repo or the 'base' URL that was
used to access the repository over the internet. The use of the 'base' URL
is deprecated.
*/
bool
RepositoryUrlUtils::EqualsOnUrlOrBaseUrl(const BString& url1,
const BString& url2, const BString& baseUrl1,
const BString& baseUrl2)
{
return (!url1.IsEmpty() && url1 == url2)
|| EqualsNormalized(baseUrl1, baseUrl2);
}
@@ -17,9 +17,6 @@ public:
const BString& url2);
static bool EqualsNormalized(const BUrl& normalizedUrl1,
const BString& url2);
static bool EqualsOnUrlOrBaseUrl(const BString& url1,
const BString& url2, const BString& baseUrl1,
const BString& baseUrl2);
};
@@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -59,7 +59,7 @@ ToFileUrlProtocolListener::HeadersReceived(BUrlRequest* caller,
int32 statusCode = httpResult.StatusCode();
if (!BHttpRequest::IsSuccessStatusCode(statusCode)) {
fprintf(stdout, "received %" B_PRId32
fprintf(stdout, "received http status %" B_PRId32
" --> will not store download to file\n", statusCode);
fShouldDownload = false;
}
+87 -25
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -15,6 +15,7 @@
#include "List.h"
ListTest::ListTest()
{
}
@@ -38,23 +39,27 @@ static int32 CompareWithContextString(const void* context, const BString& str)
}
/*! This tests the insertion of various letters into the list and the subsequent
search for those values later using a binary search.
*/
void
ListTest::TestBinarySearch()
{
List<BString, false> list;
List<BString, false> list(&CompareStrings, &CompareWithContextString);
BString tmp;
for(char c = 'a'; c <= 'z'; c++) {
tmp.SetToFormat("%c", c);
list.AddOrdered(tmp, &CompareStrings);
list.Add(tmp);
}
// ----------------------
int32 aIndex = list.BinarySearch("a", &CompareWithContextString);
int32 hIndex = list.BinarySearch("h", &CompareWithContextString);
int32 uIndex = list.BinarySearch("u", &CompareWithContextString);
int32 zIndex = list.BinarySearch("z", &CompareWithContextString);
int32 ampersandIndex = list.BinarySearch("&", &CompareWithContextString);
int32 aIndex = list.Search("a");
int32 hIndex = list.Search("h");
int32 uIndex = list.Search("u");
int32 zIndex = list.Search("z");
int32 ampersandIndex = list.Search("&");
// ----------------------
CPPUNIT_ASSERT_EQUAL(0, aIndex);
@@ -65,28 +70,34 @@ ListTest::TestBinarySearch()
}
/*! In this test, a number of letters are added into a list. Later a check is
made to ensure that the letters were added in order.
*/
void
ListTest::TestAddOrdered()
{
List<BString, false> list;
List<BString, false> list(&CompareStrings, NULL);
// ----------------------
list.AddOrdered(BString("p"), &CompareStrings); //1
list.AddOrdered(BString("o"), &CompareStrings);
list.AddOrdered(BString("n"), &CompareStrings);
list.AddOrdered(BString("s"), &CompareStrings);
list.AddOrdered(BString("b"), &CompareStrings); //5
list.AddOrdered(BString("y"), &CompareStrings);
list.AddOrdered(BString("r"), &CompareStrings);
list.AddOrdered(BString("d"), &CompareStrings);
list.AddOrdered(BString("i"), &CompareStrings);
list.AddOrdered(BString("k"), &CompareStrings); //10
list.AddOrdered(BString("t"), &CompareStrings);
list.AddOrdered(BString("e"), &CompareStrings);
list.AddOrdered(BString("a"), &CompareStrings);
list.AddOrdered(BString("u"), &CompareStrings);
list.AddOrdered(BString("z"), &CompareStrings); // 15
list.AddOrdered(BString("q"), &CompareStrings);
list.Add(BString("p")); //1
list.Add(BString("o"));
list.Add(BString("n"));
list.Add(BString("s"));
list.Add(BString("b")); //5
list.Add(BString("y"));
list.Add(BString("r"));
list.Add(BString("d"));
list.Add(BString("i"));
list.Add(BString("k")); //10
list.Add(BString("t"));
list.Add(BString("e"));
list.Add(BString("a"));
list.Add(BString("u"));
list.Add(BString("z")); // 15
list.Add(BString("q"));
// ----------------------
CPPUNIT_ASSERT_EQUAL_MESSAGE("expected count of package infos",
@@ -111,6 +122,52 @@ ListTest::TestAddOrdered()
}
/*! This test will add a number of letters to a list which has no ordering. The
letters should then appear in the list in the order of insertion.
*/
void
ListTest::TestAddUnordered()
{
List<BString, false> list;
// ----------------------
list.Add(BString("b"));
list.Add(BString("e"));
list.Add(BString("t"));
list.Add(BString("h"));
list.Add(BString("e"));
list.Add(BString("l"));
list.Add(BString("l"));
list.Add(BString("s"));
list.Add(BString("b"));
list.Add(BString("e"));
list.Add(BString("a"));
list.Add(BString("c"));
list.Add(BString("h"));
// ----------------------
CPPUNIT_ASSERT_EQUAL_MESSAGE("expected count of package infos",
13, list.CountItems());
CPPUNIT_ASSERT_EQUAL(BString("b"), list.ItemAt(0));
CPPUNIT_ASSERT_EQUAL(BString("e"), list.ItemAt(1));
CPPUNIT_ASSERT_EQUAL(BString("t"), list.ItemAt(2));
CPPUNIT_ASSERT_EQUAL(BString("h"), list.ItemAt(3));
CPPUNIT_ASSERT_EQUAL(BString("e"), list.ItemAt(4));
CPPUNIT_ASSERT_EQUAL(BString("l"), list.ItemAt(5));
CPPUNIT_ASSERT_EQUAL(BString("l"), list.ItemAt(6));
CPPUNIT_ASSERT_EQUAL(BString("s"), list.ItemAt(7));
CPPUNIT_ASSERT_EQUAL(BString("b"), list.ItemAt(8));
CPPUNIT_ASSERT_EQUAL(BString("e"), list.ItemAt(9));
CPPUNIT_ASSERT_EQUAL(BString("a"), list.ItemAt(10));
CPPUNIT_ASSERT_EQUAL(BString("c"), list.ItemAt(11));
CPPUNIT_ASSERT_EQUAL(BString("h"), list.ItemAt(12));
}
/*static*/ void
ListTest::AddTests(BTestSuite& parent)
{
@@ -127,5 +184,10 @@ ListTest::AddTests(BTestSuite& parent)
"ListTest::TestBinarySearch",
&ListTest::TestBinarySearch));
suite.addTest(
new CppUnit::TestCaller<ListTest>(
"ListTest::TestAddUnordered",
&ListTest::TestAddUnordered));
parent.addTest("ListTest", &suite);
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
* Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>
* Distributed under the terms of the MIT License.
*/
#ifndef LIST_TEST_H
@@ -17,6 +17,7 @@ public:
void TestAddOrdered();
void TestBinarySearch();
void TestAddUnordered();
static void AddTests(BTestSuite& suite);
};