From 8a8a1b83be4fafe4ab385b959bc08b257a272ec5 Mon Sep 17 00:00:00 2001
From: yigit This can be:
+ *
+ *
+ *
This can be: + *
This might be: *
+ * You should not call this method on UI thread because it may make a db request. + *
* @param id the ID, returned by the addJob method * @param isPersistent Jobs are added to different queues depending on if they are persistent or not. This is necessary * because each queue has independent id sets. * @return */ public JobStatus getJobStatus(long id, boolean isPersistent) { - return null; + if(jobConsumerExecutor.isRunning(id, isPersistent)) { + return JobStatus.RUNNING; + } + JobHolder holder; + if(isPersistent) { + synchronized (persistentJobQueue) { + holder = persistentJobQueue.findJobById(id); + } + } else { + synchronized (nonPersistentJobQueue) { + holder = nonPersistentJobQueue.findJobById(id); + } + } + if(holder == null) { + return JobStatus.UNKNOWN; + } + boolean network = hasNetwork(); + if(holder.requiresNetwork() && !network) { + return JobStatus.WAITING_NOT_READY; + } + if(holder.getDelayUntilNs() > System.nanoTime()) { + return JobStatus.WAITING_NOT_READY; + } + + return JobStatus.WAITING_READY; } private void removeJob(JobHolder jobHolder) { diff --git a/jobqueue/src/com/path/android/jobqueue/JobStatus.java b/jobqueue/src/com/path/android/jobqueue/JobStatus.java index 26c6063..4864411 100644 --- a/jobqueue/src/com/path/android/jobqueue/JobStatus.java +++ b/jobqueue/src/com/path/android/jobqueue/JobStatus.java @@ -9,6 +9,7 @@ public enum JobStatus { * As of v 1.1, this might be: ** You should not call this method on UI thread because it may make a db request. *
+ *+ * This is not a very fast call so try not to make it unless necessary. Consider using events if you need to be + * informed about a job's lifecycle. + *
* @param id the ID, returned by the addJob method * @param isPersistent Jobs are added to different queues depending on if they are persistent or not. This is necessary * because each queue has independent id sets. diff --git a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/AddInBackgroundTest.java b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/AddInBackgroundTest.java index 20be3a3..454d3c5 100644 --- a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/AddInBackgroundTest.java +++ b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/AddInBackgroundTest.java @@ -1,8 +1,14 @@ package com.path.android.jobqueue.test.jobmanager; +import com.path.android.jobqueue.AsyncAddCallback; +import com.path.android.jobqueue.BaseJob; import com.path.android.jobqueue.Job; +import com.path.android.jobqueue.JobHolder; +import com.path.android.jobqueue.JobManager; +import com.path.android.jobqueue.JobQueue; import com.path.android.jobqueue.Params; import com.path.android.jobqueue.test.jobs.DummyJob; +import org.fest.reflect.core.*; import org.hamcrest.*; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,15 +20,19 @@ @RunWith(RobolectricTestRunner.class) public class AddInBackgroundTest extends JobManagerTestBase { @Test - public void testAddInBackground() { - addInBackground(false); - addInBackground(true); - + public void testAddInBackground() throws InterruptedException { + for(boolean delay : new boolean[]{true, false}) { + for(boolean useCallback : new boolean[]{true, false}) { + addInBackground(delay, useCallback); + } + } } - public void addInBackground(boolean delayed) { + + public void addInBackground(boolean delayed, boolean useCallback) throws InterruptedException { long currentThreadId = Thread.currentThread().getId(); final AtomicLong onAddedThreadId = new AtomicLong(); final CountDownLatch addedLatch = new CountDownLatch(2); + Job dummyJob = new DummyJob(new Params(1).setDelayMs(delayed ? 1000 : 0)) { @Override public void onAdded() { @@ -31,9 +41,33 @@ public void onAdded() { addedLatch.countDown(); } }; - createJobManager().addJobInBackground(dummyJob); - - addedLatch.countDown(); + JobManager jobManager = createJobManager(); + jobManager.stop(); + final AtomicLong jobId = new AtomicLong(0); + if(useCallback) { + jobManager.addJobInBackground(dummyJob, new AsyncAddCallback() { + @Override + public void onAdded(long id) { + jobId.set(id); + addedLatch.countDown(); + } + }); + } else { + addedLatch.countDown(); + jobManager.addJobInBackground(dummyJob); + } + addedLatch.await(); MatcherAssert.assertThat("thread ids should be different. delayed:" + delayed, currentThreadId, CoreMatchers.not(onAddedThreadId.get())); + if(useCallback) { + JobQueue queue = getNonPersistentQueue(jobManager); + JobHolder holder = queue.findJobById(jobId.longValue()); + MatcherAssert.assertThat("there should be a job in the holder. id:" + jobId.longValue() +", delayed:" + delayed + ", use cb:" + useCallback + , holder, CoreMatchers.notNullValue()); + MatcherAssert.assertThat("id callback should have the proper id:", holder.getBaseJob(), CoreMatchers.is((BaseJob) dummyJob)); + } + } + + protected JobQueue getNonPersistentQueue(JobManager jobManager) { + return Reflection.field("nonPersistentJobQueue").ofType(JobQueue.class).in(jobManager).get(); } } From bd0d0740d76201042031d5ec25df60743d70dc5a Mon Sep 17 00:00:00 2001 From: yigit- * You should not call this method on UI thread because it may make a db request. + * You should not call this method on the UI thread because it may make a db request. *
** This is not a very fast call so try not to make it unless necessary. Consider using events if you need to be diff --git a/jobqueue/src/com/path/android/jobqueue/JobStatus.java b/jobqueue/src/com/path/android/jobqueue/JobStatus.java index 4864411..b687cc3 100644 --- a/jobqueue/src/com/path/android/jobqueue/JobStatus.java +++ b/jobqueue/src/com/path/android/jobqueue/JobStatus.java @@ -22,7 +22,7 @@ public enum JobStatus { */ RUNNING, /** - * Job is not know by job queue. + * Job is not known by job queue. *
This might be: *
O(KQ%An>4%cDST>@~&74Zl{1mEkXEVt7jfO7|_C#=ks<~N1E3-dd z9qn~MPSEoiE>UWqUA(KL#Q-MurE7nxH_S+FA25TbvWkZ}*8HNVj^tZ7R=h-$QaqQY zlM;O5?N+dZ=cPqE@}}AZibpMLO`nEc^Y&;^n3PLhyv)PH-4Q&p?wn>>;u;mqxC{*y zJFao4I#f74vU#W3H%_)UtuFXq$XfxSC|+6m1*M}im_6&DaXAqq@;?u8XYrceVyP}w z#Fx`%3{x|1G;_=f72Ui5ejJxJiW@F=zijT=G>-*gO?}u=Bwq`7*){XtvMy8Gg~t@p zH(XNd5Dc4usl=7Gd0#PxSl`e*Fm^EWvmS!Eo!@E;teuWOvo5$FfOC-UC&Lc7ehm1) zMDB2bI_8QRInX&{{5W8eoF?xBqj;l}gj-1jgb%adCJ{Tl&|!#Ym?<~p2G_bH6dSWr zSwDgMT2d7X>z|3<#wCMIK#uwVyE4T9*qY|K)e@~7tu5=+7U-ehaTd$0=re~GG|0;w zn(1QtNXrxoDaMvftH1JkJuxOZcjC|+%WUZpQ#facxj4d;jRVyix$Ge-PwLF*PILR$ zu|tmQV&Q(5I(`M|bt(@#lKQNQ$)DF@!D|LeSgnUd%|*-32PxWHB={GuvWjv}CbLOcpbin3 *2&ePe3&XNH8zy*{4F>b{+nOlTxZ-BTK zNrx{u!0PBRW2k)LHxFIxB#es%s3ni7 JJus+vJXE)ekstJ91;;9%Oi%s? zzm;xnz%3t8U%Iv>O=o?aQpYUT70R#JJFA0`!fp?JdhxeJJ+H)tV|KS${gy!IrHj9= zU*lA^Aw87^M5!siX3v%WsCLbdFY{v3eT6GMOtvPGmnN2vOwC2HF?&;D@(u@DT23q1 z&h3GxHZ3nQp;ceaAn31X1| XF|PLIPGih zDOrA-;*kZb!yYq8WPCIkazeeya^VNo)+Dyn|InJ1Gip@%KAOP`m$$xaimNGu*Fq_q zM^rt5* x`##ZGGBFYjV_RY3x`w3Sveo+A7UqSPQk7q0H zXUpU?{Quo{;2+Y@|915F)HrFNX`p}bvk)^!M^b55P*v84QuQ}f22z1)TZ=(JG&H(N zWgEGsM bHzQfsFj*|(A6b>f#>0ESNKKgrj zU5*n47`!5M Tnua1=56AXle$ D-qs?@Mx!E{i`MRov>qVluhru$?9Liuwo`;VRZl;R z0X4Hln@#$bX%9CxId0p*xPO$|vdKY#nK1)%d3lm6Ui7UiyF!n?hRk^R+Hq~cI20af zS(}9T+%YV}m95XPjbeT7m-&y_ExFL=tjKX^oyB`-s!H`}z6`u+`Q;4UqofCc$~eFB z@S1Y|pOv?Kw5sW4AmDR|v&rt|dE`lnWU>~9s%@b|wBQmGX;7!ICYX+psAwqn{alXZ z-j&^;i}9cq!MylZHnC=P&)?07(i&|9R+Cv$ZR =3d)Z>Tw0qaKo!GPbfP{5-RV4&GSUSP3`QJ>(?3inZ3g z*XFX4 Lal-i(c$_n~)p@=#805eKIHJzG<#6 zmFY-~C7vjZrLOM_Wtt)07zk^@wc0?Qb~$JjPDLl&ObAT*-)Dec_T5$GR+Oa1NR6wq z$i*ujv(g@(svY>r#cc!(hn <}IS&R}so`dE6Nz z6lrqk;oUnrm!iG}jdS?R5^fYVwMWAjZm;U2d}V_wuE@|0PAl9u;Gc?uSXqm>f%-L) z>*;nCy!a6ElY>+Ti~)}0vpJ~Z^5W_tbujun-c(vtO+q^N@x3o74J`c2siW$l3 |sc2!caa8eAfcYD&$A54iI;|uwPa|nuC@Zfq z-2}X>UQE#X%zECiNCfT2Miq+0_v 3DVCM;qfRz3iFHJ&f*{FDYFKBvnT-!Co< z^{x-|j9Zg?*Q?2S_oV|B$K=uqT4JphHWA0E1*#5#F(0q=2bN_j1^f~1+l^M&!yb%@ z-?;lp=B5s1!wFAP6h}s%AD~Jch92%ddo#RokHE%7KTd11D>$c05Xi5TySVC|;YsSP z;mPw}m_0?*MzE8oFgzI(8^IrybYyIVjviIY%?F~cL2!F=pj`(OF^E&o$l8@r5ey`c zd#22i3%A=4KYD1>_a{KDlGRG)3YlMF{fHChk_OgLRIB9*e8J(QziJ-Q9K;kD&*^FT zBMR*Ep-&!=lXCHpzQ4h|O}wyS-sy{&EtD7&mkudQo1kbFIze4>#?qwD8vu=wPV{nl zeO%-QY73K8=Rq@*Y4&MSVWSO6<5FSXVrS$xsGrz_4fe@*7(&h<)i#{g=6&9C@Koq7 z-7}J#8!<-LON$+;Gn?tGi-SX-HbwK63r(j|oYrKwb*6QSqRkV>U`~a}8xWwCVQdRY z!WDOrR1#z3AJ~en;fpRot*VUB>=i*B@^J!;qM!}SHNRulLa&PPY6Au8qx@~LC~HLF z^GQ`e^zOd+|4iJJ6F AiLS$*mz ztc>0MPj7yTvW_gCF#7w1`TD%;d~=88DZR8``0A+oai%hIks>q=mB5oBZzlZkP%9oq z=7U4Sl;Tm4V6LEyw>f-Kt;kW|MAoWDM&EnuvloIP+@u9lh=rM_CdYInx4K`&YYFOG z;|bPbnPj?+e&i&Hy4Rb2k+a1dkK)fQ488eHPtg)im-PrC8~6N%m;k9KJ0nTVfV4AjnwasnHriW!hk4Vi ztUa*x#3pGjikp=!(B$i{Lf^H& F`pNFTY3+OI~&lJ7GH>nOO#!vfi4QwGIK-4 zvwpMie49*0#zn#p89-Jn#g1z1%j~Diammu_{xSMka0Em~|MMNK13w?V1Go3jfE(0p zYTuZzDL^3_)iWF%qe?X+rdD$ciGxKuHYU1t+B=$(v@+dc$A{)6c4kp @c|CCR+nUyH+LXh;LX6drFiP&v-c4@=5D?=3c_I9(u~CS+ zl@8vgrdCqw=k%ungsC=~0BuM7jNINt@>>>eG#U_G1zpOl$qnil^mh@CnfYE(W?PZf ztgCdUYmxL~vt8>_DZQ>}qQIXmq(6**;OsVep`{J^8FpG+F1a7+ZaWXNTRvVU*O5S? zw~2UOh)J;f^YK$%5l3-vco??wBLfjJ#0fBM3CP>Ypd>Gx+&rnun+tb-$8zXSqX5+i za{3O7Uf^&$OLk?ODZ&^}pI|$213x&|@$ycqt)5zA)2 URUH+2KY$@UZEQNwiarLwPw7BUP*7vE z+U}_PdBl@5rsH+l94H#`YIl)#H%SZqZHarpGIK=7xK&pdc>?Z0q$keWV!XHgcyGUz z2HtzrE5F8L#649a4WCs5xgd=IK$10`^k7zQn8{7iL}{ZF^<4Cuw6SlucsVgXp+R?E zsHfAqHT6p3&|GC+_4eVY)K~b)s?40>5IU{4K&J_{FUzr`UG1J=-MU;Tr#%saywp|g zO-%UJd?|S!W{?GLtoRJQkvluzto{u4us^XxS;7-*Bq}Np9BLwlmAqUx3xnlPW1 n2@PZnSZ(lGP zK0b(eaO}i^jPUItO=@p5-PKn%U|l^92w4Nt7+tJah*foLoZI-1E$A&0+ES7x_iSaR z-#16Mk!ap6xAfkIyS1`N!Eg9O{XkXUmb(oc$GQ!iM1k^Pm)j1I`rY*E{o#?TkTjZV zPJ`W>PK({R>IfCGn!bb*3~E};W#u$0c6g)4dig6C;rn&@E{)>0FYR~uj*>{t9T8v` z%pYA{MkAz()d{R{Segzr8=O~P1PZsoEWdin*O#_>Uj}jEt4wLd>F_4&{9fM5aHNB& z9(hoMfXZa61GsIZc`6#L2p)JOUaWYnE;P7lcv1rMwHU6*?%7-m>0f<`Bur9juFP5i zef)T*59219mm=FcK-~?eKN}wIv%(Ew)X-iWNC81c9WZWW7Y?D
K;c$i=;OvpB~NDfGAKcpUnuI~z^h73 zrG02H2eUo$Y#EtGy|p?4cz!vK7+c%f9=~%TizUr|fVQtBM5h^*PK{n|1BqYQp2M_z zBt;+pG}&59={Z!HmfsG_hVF}35w-|&n-{lt^le{z9r%?}mEb~+KR)v^7kAw7#;`?r zD_N|QZz64ykEfo{VK+_kkDgU%c=&Y*IE|jn&Y)s0chQZA2c{)`u`4D<#n+=MtWRT` zb*91Dr{(~=le<3ZcR5J-t3iJ~1dqtJqAFU<)}SkXvBg~TWHn0< Y7XhM>&_8$1 zQ*kD?OKQ7R>D|&7sRD=1Iu{OjWkS`t_rM=Ld1~0J>-V$kAcsK+R!#FW_I+eYDsRiI zo~fzS6G6#*gU`p~h9iP7Kt$_>6f1HZ+zLZUmiACfa|Ama2|EMvL03LqVLBYL&|lK* zgwbxmd06*>APiSZQG&3Q#fCym?)*Vfr^>)EmN-dJhuvCxKZ`xH+whG_4 2tbkTYTn*6dR8fx(QEs!e HD3};_l zjy3dz)g=^#%^GnRt>PHELoh_EFi#fJdaZJ?S*O|=t6R^X*39Vc`Wur%j&SEBjm!Ae z@(`V1`~1usC;Lx66tJc#7K~F9KLH}2 gF)p=XW^XWG&q z1OUGA(nzmcf!cz#Cvag`)bD#ZHv63luND j_Q;ltC3yXkxgY-~%(7VK>QMa5x8FWLf4O!3qob4lpB}8 #=_&{v4w$=j0_pxqTlMx-K#&&l(D>N_ z9?d`^J09T2yW%(4Y@~6?u~i46Y#)Ik;S#hY%HUQ}avOS1M|p#?Yq 1^fx=|uY9TH_QoI|Z~)+tE~g z1Fq31&Afb(F9{DT%0l9W4KT#G(ln@6Qg5Ce*@p=aGF2MMg4+%~jO5e(L|btz6BoZ4 z?!HkGwX*VZvQDzr)vX$Ib$x)>#QuiIDOW-_jW$#ppjv2doM=Gk OOyq@>!R z!|POX-UTi&!SOaUnw0;nTeap;E1yU@Z>&&RcXw?YV)El&Tpr%^#p9G#EmMy&8^N5{ zg%jn`<5pLg7ZGiU?j>xwssPI6T M89SbL z=D#_?)ApHS@il_AT|go3p*kIXbq$-;=w6Hm(Zp+I-n(#aH9O9EtY|FI#&zRJK z);oMGI%)jyS$1eO#`aaACTiqq1axsc>oV%H50>Lo#&Qi{D=)_A;4{_`NTL*RU65c% z;^I_wm=0}hT`ZK&}j7%!A`JA^#giP zYXE-7!$fl7#OYyq6?_E`2^KNu11Jdr-LH4*!*0aXEw)(O3?zsA-<`Hog`xuosj|R| z))bM#ZCIt0U$Dj3`sn^ZI9^#LR%zrPy5+PJ#xo@NhI7wIFfoP~@fkyrd1Y0YrZD8| z^-n-W{=&dtJ9CZ;l3T_)dhPmchUA6pptMEr8+ulcFB``8qoooVCeiSYIJgUqSa{a8Gc^T|Durk~FGK {Bibl>ZX3qajYlcJ%|7BDx{$bgm4d=e! z8g3&xsfZ3P-h 186*=8*`F#_37-`g}66d2mr}N=XGBMwH0>&Y@Qdicq+R zcHMZ2Yb*irae|1nUc_$Ec0Ot0hm`aQH<+)*5yU2Fv@ayKPtHrs8^S$(hVS3JivQXn zHh07&i#~UV$4>&6?>~2l|9nI8|!lNKhiCvQqL{Ow>Y|YnIX0WfT*z)FYc<#pdV=|H{Nwr^rJ>^9o zT8%Cl4jWp{WZ&LpZF ~+jkR}ZU7N?LFBUm^XdNby^uWd(NnNMnu+`_hHy&q;JXNn77Vofy zUVqFKfYd6YvI%8c^MRMm5C&O>6*XNiQxWK!aV(gxIC-GeXFoQrx1Dd++HNGi-(uS7 zWT#rMJq+E&&fg}zT#d;s+ke-(yDL;ymVd7;OEPbJ=Z#FH?%L&Tw{)JSvTx;RsWflG z?p?DVdW-EamD0|KtDOg%2IJ9uL`z+&=@%{$Tl6Km8|es0zW||@bxr3(bt~gfsuXj| z`~vR)iR&GZlh}lvhvagjZk#R<0T P#JAe%l_ z6U~duu>Cs}Lry}t0@(8qp6B=zlXWbo^V6&H&uR~oOW^iXOf1es4`S&?caywcXvS!k zw~=Z!MkRnjH`G FKBt_(w0*w7<~!VldO1p@q{aV5tuSp0}~ z(%oJX*VJHSE`lf n=x}!`S^vDVylr>i)B0>Fqj|4`>ZuB`p0r}q^-M>_YKa(Z;yLqdb(Yi>f zB1UDdDiyjGEOa6&iIuU4Z9lkZev?&3GWjUO@;d4VY*xEh4Hjd(r7_=YL9|1o4TiSL zZr<%i`{TIJrvEKqGr;#9HaEB!GL*@pTQMruivSLijjjwhQ(HJ=D$A4VR>1^syHZ79 z&rAmE@(QmF R|?T`I#fke%QZe)7e+B3$UA`G+eQZ@q%qqo>@1a7pYe#Xy3Y zl)>S6&3n$QYmuf7U1+VoH8bB?02&)}tSjEO(Osw|F5u-?4=TS%ki-rozZQZnjBK0H zik7LBc6?SB_=gzcAao@d%@9K2Fy|^rv@yJOl~nfAl_VJzqV4Akn%WRu6cCcNrGIPD znRT{c8fb7K^P=^rT4N<^SirJ$SRzjp7eT7$iMgB&fNqX3$`*-5G6{{#?Uq#9!Kt{a z4WF`ZvyzkYYI5WyX^G+8b0TR9gjpl^=H7FP 78?{{Io#!8lsmmkOK5;n-)gn-oH z@A5B0IAfDV0S=dFMLH*KSrtvW>Usvz#qFDR?2k2m`4uQllV0o#8b|U5Z%(SyE&m%0 zok#=)=Y}v|+&kXBCVVjc3FcqZ+7T(jjpQe8mi=7s{{tBO+gktJ*4^wr4d_2Tw9Wo& zpZ~}Ho{}&w1ICOVqVU36(9o!DM|;wHQLhHIm=YKs$kbd@^y-QWNXQUlVLh>Fp=rMQ z;!DLWEsH@PsI|72z4p3}cJqGy0=Wf0jtaZPVzes0E*{j)eHd8GANZ0Npg>fLrG(=B zN7V_%JuVbHM3&neTrCIAJrhU@fOJK-DmobbE+JahJbY+O{Oc-st3QqAPDFeVWilSt-~=BqaAXP6Or4f4pl{Y{aCoSq#qW51jeF&=@#@-miPt z?Af;23pru^vp KiMKM6hw1bzc Ij1k}e3D3hm2Ppi1?Le~jpYsv^u?_tLe*7&1 zuWQ dXu`a}KL( znU1JP!!JQ8ZRjnHGq$)SoFOuBu;E7mIfIAS*Mfo=lNlN7w!bs&dlf!>T{>@nt$NsR zE_YmQVi`bA#deJ@1+J=3CB{!jFv9?%k?97=yq;khIT#Tc8D@*gqQjVpx5J}pom4{u zS8-mfu?@<*7q1a0UI21CUBFddGGCnmZ(q*n%i*Qa%h9JV{zH_4iB6RMW5I$jyT#Eh z<)DIn>+Nf#+lRisS#g&sPVkS?%?1(#6-@p;o6`D((7S1G{KUH&p7B;hW!-(vvc04g z2TCw;(w@3PwTV?m8DLd*>7=OHU&lJ};q83nRhoQIf9@W|3fbhC#ns0yY4hbZ4WLTc z#IbV#GOh*s<{oVt3s%`%-gv=B{18Ajbc5#)iBBSz@PC6u6_aH5Nzs|bmbbJ9C-t9a zGIc}$NZn~Xn5rw?)g}QpRh;ow#Mo3)FG|%v;C!Ck(LzR}sJN(bz*K2Dw$E--vbhwp zH+x`3xkw@u1I~9iAL$KGeuY{!>wzAgL?tn@!UFjX)}}{Y%=>bMN(Lj7niz}{-{+Qy z*9CG3WRNLlEP(a>x#oE7Nb$rEsokxo@;1G9l|Qk@&w&V3V=|p7^mVI*(lN&X+M03! zL0yDV`IMT}6s+OT1D!~}>Sg;b|9
U1WvCs;t5j`i|S-A zYFb6(Ip~V<;Zc2!Cicahv9g*qrS%Q0 Aozr $l|5FS3~_*vAb=r~H->K1DfUZD_s -ASlm$UWiL^$ zn;~!$X}TvL4M6BhyMltKu0z`BZt?1T9F^=LT0VE!X%B?+lr{V#5~)W`in3^TQVS*F zrq;9igvJ i?saYT@nc2yPlsQF}Y)fwCcTLPp}>w$X8>{cVH z(|~iOt92VQ(bfZ5Y_A|Bo2t)(6mjsrp1~@i-JlJU5Uu>R;5-t_9K-YH0n}lxr!z!N za#H p(ABEB8*UcdPfYTZUjCvUb!UmZQboR5@DSU `4OutC5X`9KSe4n0WeNH=i{aMaU_xIM7}`}g%0*3xx#@3{H*~wN zfjN!swT87&=_8BG_LRRe(*}eFl*S-~5;&AM;|1sJ#~|5fi=6}UI08`u2`jzExC_L| zh-5$cWNyC4_#VKcmd<8;jw=3dPhb1yQWBji7;R)N!NO&LM%rygUG4CI5AC4OD{5$> zP_$9Q1trc!{P^Q;kB%&bTat2D%a&oY6zh8JcmL_gtOBGqb-;23wIM7w!*ClolGR@H zsV!{^K1;K)#^mkC_lXT1ZX{v4Mgj8}3ljq$<~UPj2V|N*WaouB?fcNT)bs&$aKU^Ybw#*3n!3QbbbI!KEb(;s^H) u5fi=8oLu zl_XoR!gY%&e`3b1=Ca<73_8 XP$z3(R1^(A8zHG^e3q9G=pAyGpc+_Ah7sZtBX zW)Xn{q6WGdx6Q+46#1=%!|f8uosbJ+ GKRtiv@Q{S}9 )c%&HKr}Rua?;k6Q9>+JV!Tfa}}NX z;Wig)kv_7?QXy$!6;x6bx$ao@(%7>~GK=XSt1>E8U@*XH8@Xhx* ^e$Z`6>Tvmt z*>5IJR`tCtHj8_*>BW~ij0g%uegqKxo}<(D-`!y=*)*i&ei1)J1Y<)UuLD!gctfR9 z!9YTt@O(i_#qw??No1#5`TQcExPv{;;3FoFTE925&H}X2CKC2uFP2fZ(PyMtwHbHB z-oS#+D1V=&5xG$tA{ICwBVN2vY7OuwHXn+v4Prl=9~ bB_o#I2%kSn1&ixoLc4>1f;+8~8&NsvbB$`VK{ma#@iKuGoCsK?IP`Y@9Uyq~G zIZ_D(K~}ky=b?xMRmMxzF#1x24zVS`ewj$U@ !eyXixZFu}Ced^4HY zVN>&p;6pXGHFx5#c>A~LA@Ls8PW)uRGCt)s6#viY`A6OPPr6rK$7NO>`8^qXv{oDy zzJHtW>vt2hHB8XtLPh8%A*t_$(xSQIwQ7>83A$PbGp`+g;M(sh_luC~ReB4@TwnS{ zEnX;JL|6PJ)lHXw%L0-jCbH+V*Bv~%_-}{;K3?#?r(N6O^D>c}@WgV+xf$|q9CgE+ zhui@?$H*PQZeiB?>90Ly2UcO$LTOBdB?Mw8PD%sg*@!zx3-dkc266`-C8;)JdXUS= zofgoMWF@2rd{9lx_l{DvQm5GcB|FLwXkzX60BjbTB7@2LY-+{EHmDB^T?D1}2=jCY z0DK+Js6I+B5v6luGilae$57;iE66pt>~qc;6q&egq&QA2N>d|VIvzbzQlaZAT2nHO zsTv`}71o(^GU2D@oxg>CElF`gw_56?%v4;^$#AVful$Z`$*;tOr%&U&TPta(FZ>ze zv_40%K4gzNjq)k)O+r;Z28m{tteb}r#Uo5lK*ni+{YizR@J%%plg&rF9j&{v-FmE~ zk>Mem#oEiN;rNgL7hT0FmBHUQ>^pQPXT8~2CKc7645^5MpJQV;*c4qHCc#+Mvicmc z{#@3u8mOHVQZQA0wbCMqt+5h)&O dGoS0XcpG`i*I64D6;Q4WXUbba zJhBVGyzgl#UPFU3(9m2*#9>m^wICM;(sjn;S6#tzO*MO-9O{BqU(E$XZDTPM7opA{ z4Gm~1MjY*KvQ8c&ZQ)*X&Zet=>N$gw@g>FPKAWGvO hhv2Q1mC*VT)B*btE-7J21ADQP9sLSRXeih7`XnUc zVWME_Te3y!JBGNR8+U5f$UC3E3j1Kx6{v7k8VDxx0-qCgj&;XuU?Z|=NW|En13QCl zxP55jLEvU!tlFdTq}{_w;=UP0qZa1tyd9hBp{qPrvEyzqrpe<&l#$)$9m@}E0D)@U z>S0wo<<^0uYd&0)nuB}eBhofmf- ^Bo5w^If^c{OXFxP~~mKK^oO_qGTu*Y!L z%8K7!Nt87zu&1IYJ^1{1`r~&jx4IfR2_`H)F``R+WkISDa#C&~+~`7TMH{aB?QL7o z<_lF~pqXJovrR##Jo5PF<84ls<38BNNR^8`EW?Ytmj3`bs#p9Cv9-S>FNrRrS+>Xs zpXlnEO1+>k*)8h=zi>Sup^i3Oy4l$gf&i7!7%!x`|286Gj4>R(O3143tRUe1C1PVG zVb=x8*!QCF1HE-Bitylfre D{Z2`&Rs3%)nL8JASOqcJ6DRD28^4#x J>lbbt8X>vG6zjuYz_4l#fZiW7%3zb$agGnFV;+{)k z-RRO2!OrqUr G=+0`fqTJL%T+`5!r2aX9{B8(6t)m|__$0+whUC^vS2*w7L9)0()GS(jb zZ;eMq=``DCB+uGz?!C0`oG#JM-#Wway@I7);}FC$VdvUbs~?LF{Fj=Dw4XBUP7a-K z_Y(8=4l>rpF^37R5@lcIaBqZ$GltL#MJDWLGjO{}Up)u>2qB6Ym%043la9fv*a-Am zu4^`&T`AXmOlMs3y)dL &*`%^_B)7OG0VRdu5>5+%*of~v_pFDY*Tb?_|m zhF^AlQ)bvTTHhJ}mG7NhB!e&hVf&hgOfhVw+f=CaJFDP(U?t~M++77cv z`90p>{``U8LN@R To*>_X2 l{e@+adQY}}QqJyOVaVceUDYX@~IhHmGt`0j0b zAv<9}(iarLo~qDJg!SrP@2RDX`AXp_?W>SQU)A)1gsz{Mp7*t`2eVc%tG s&*WvM%N@t0s+(uEg9L$)nn)R@rq^;*yQA{h{#-Eh;eN0KCssN_jn$h~_R&eB3m zEJ!r?TOoo?nav*pdY+|!1T80<`hEOBLO+xMe|my$uIHdDYz|kTW-#Ig=8t31xtP)U ziyTD*m%D&bq`gvnG3vFcsg=cV2-Zj=W=FP06ZZ*}bjbb`Vnz>t>tz3{ZX{(3Riya@ z!_CP5V@m%o=GgRal>a{&evN<1W>>>M0oTB9apMXu!(h4sE_rPz6z$I$Va!`(g#$}= zGhbqWih0BZD9lz#RnlpNvWfQJ9#a_XGw({zTm4&oZF1g2{ZG^0BU>*!SW!`NtzUk$ z9By#0zGk~jh6lVI#sxq+!gtNb_y3NJvxYWm0n xh?I90_*nj&iR@y@3U$+bQkQdgzBSh;_{ z7>9?#5Dw@xZ#xhxdJ49K$q$MmGQ}x>$yQS+fxWP~upU>7w`S#1sv8kkNmg$tE2b}& zq2JFhu|;n0MkH9RwKwb}kd !?Z{VRJi_Zqwz!B5x6CXtPZN zjIr0PHTMr2nn5ggW3A@$;o`Gm9|Gws=JL+oTJMtO9_Lbt6HA(8Zn3%7+?x@;>q*p7 z@n>_6u)E+@EoV-%U;07ap9Tx!<@!A8e+X`!#I_h$XWV-R*#(hz^SaENpZ~10%x>(r z`_|Y(gd% }$rJl^@gY!DPm&OcYN_toxYsG#t*0^;>Y5P4VD{S0 zR}{$U<_o9c)*j HiteYt!5>1v-ISplm0s<->vX$ZArM6=9Rj0k#t8wB$3F&+ z$3F<}T&%IS@M{*zzC!&Z!G!wghi)yY_Jzc(1>Lm&$d@5vR#CztkBu!UaeHm3dPy=p z%l6zDl-XI-q1U3C^8H-1$paH)?u0mtm0wLc;O59}w!E~Eetf8oyo`SwC^HS ~43NWX52IGnjZeIOLb0LDgovqPYD>%S^>16VO%vOc8+mXBpMfK+{_hxVZOrC0a z6?%MA?1_g2y#U>0F;6*K<*XU;e2CtaCVgT(uxg8DLV2lZtNQ3?ON{-=8DR{5$B%3s zDJtw3Kz^sbwv@8dq$S;}kC<|;>K>+dU;1kE?PLsZmyZSs_rxJI&$AqHV8@$QwiwZa zQT*iNE}`6xz9!dqtf@FGCuBzA7|9ZTX>iPduDJZ~*yhZ^%zPsOAQTb4S*a-R7@oD1 z*rZ{_d#mx?(P&&SQpe0$B$CC{r@9?UgLoD4$~&%GNm))Y(!X4d9qtsQCc^6a3pe^Z zm!HFt*@@r3yly5mf~FJ;eyzKIEB>=Y%v{{Ay|t-pqMOqhgFbwg85`{r947&;mpBp@ zyr+W0f5lO+yOWLA?7@)6^)*ek-U^Fwv-$RM4dJe g=#gO$_Zi z1ofP}ewfob?@-Eo8vbQcPDjUYiZXY{$dcy2B;;xMMqCHXjHM@Y6U30BbaQzjT$;C% zUNAKhPMChJFV403-EXY3CYenVwnkd+tqlAM2&raomO-sNsuDPG4{wIlEG7Q^Rm9@B zJ`N`0`wnCgo*w&}lm+%}^U{pk&Ym*ZM;O($%kFOVjI8ESI9#vkB@%PX#qru^%?AhM z0a_5svW=ve#d`mC*B-|zEiuZ(HLPwf-y|91FxhN&A0U{|ztuCN9|*1Fk2(1)w@>aO zq*{Nqu;Eu#>QT@siuBpC$5yjd;5k;E&ya--RD^c9w5Q%8@u^u-73xUHo$!FbDS)1R z)VzjU+Uy;TK8nlAV#f*PYRzy+1J~PWB|8-F(7u}rpJlWNAt7hR5}Dher$%W^vpNU* zuSw3*w<$^BA%fJ4+{&QkUP$EHAM0PWlG#=Ky;13}Y#SxaZtMFAwctLX7SDec(4Rxy zt<6kaKfnGt<^6Ao_umAUzY=8?dWFzKCLzGZd<7Nz^3S@#anam%NtL2$^U1IuMR`-1 z?ZV)}U_N9tC>Y+pyi@I>moYDi?b2NwXK`=dxdR+78LL4aYITG#@w{vgYEvYs!_M*D zp_kE6VErVdG;BPA `&6r1uk@H8_$1{h2L38gXO|`ZGhE7N+FcgR=DgZ`$X--l<%9H;(itr3n5>j7a?F zJN?@${HF>~)X2r`bH G0&>AkjqAeGVsD4k!3M9uBJ= zHoweW^Fe?qC@)RPg1&lF<4~PC0)@uARluO&hXKy2Q^w&bBb4yhjl9Y5I(qv<9<$O0 z-h=rLDrBo}P`v^u*)y+H*hm62IorZIPrrAcX0Fw8C(IbHl3DHBl32GRy6-=Yad)Mv z?cg@klCN4%y-jS+BJ$`fe^k=-ehX%uQxk-qOw)HlFbnFz4(4qg@GteDnFuk1v#=dc zh}oBF5#_$n%GE8ddes7@i;XL(%fpSq);xrLeSI&*2ankgH$TilWJHs+cqz2+HWrd< zYJBz n?6bc>B#^ohRw ~RgkW6TAU%K|=8%shj zJ6c#tCKo7mMvIJ@F&-~uHgo&iCbNNY4rhN&T^VU;$GtVPtz_K6(s?w&Vp6L>-$f); zAyjLN5G(q^bP^e)pMA18ks-eBsTgmPJEj({0BTD0mch`r)a=hWVMJZZ%`C8*5V|{7 zVwi$G6yY7yy7?6d #%1WSEnhr%ma znB9Jx!#G1j_4b?UExqv_*i6a^5}EZ~mymPg%}xlnqY_C>TQgRw6sZE? n zed<;J-wfVA7VAG5JZ&FCU9%KH3w@7+gGPDLS$9}FQ6jcD=~+zNmAi~o&=86uNNo|X zJBk+R;$LIIElB`jeIRILyKd162@*=lSE|WiWML&Sab%1XJq@R6y#+lp4{>$ODffWG zHr6DswZtAl=PsYFOW#93|IKHB-}TV!B9lLGU+@`kw14DN<1AfOO=wKa w^Tt@6I<7ynw30*=wi$(KE61tcT@_CJ6K~lnOLfn*uW>0kfWU1Qc@d9U$ zJ*tm9W!Ep6#kHUEOc;oA(;k#{wLVA+IYN&~J8|V{?~dX5MNM1&q#BcU?ApkrJK-j5 zAE@COQc(e~oFk=09yiZZz1?uLP;uSLvNCcS wt`lvG zfcZ+J8LRyIT3RH 7L;;7W%HrwlHP woi{zO(&fa*K>Me z9M7n?P0|Fno^(r4cx9RcUevj|RbiUJico*m<^=D`F17oD>Z|>cX&3jxQ6u?mPe(%~ zU8oK@CFo6FFl$`*t}}BwOF73D`V4sE<5FkL6T-FSplM0{YI*L%9A2?xwm@U%3yR6= zwtVX&;qB~Kwk!&BemJk9(?>Z_U1n8{lcdy^!e(ds?PnP9R?ght7Hk>K=)9(=S k6|gH;JpfWeB2?zs9JL5 zyVL2~FOp{kWMJCt!jS7qyX=ZhLB(om8Oc!Zd%5$n)A*pQbj~hP+>*C2T*uixw$ZWd z;j7Ut4IA-U%Ucp7yttbVPWL5+qXD!fb{7>ddv#R`LK-zCTfE~52mBv%Vh%lX`T~yH z#ZzZ&BqUD-kzHA4u8Fd h@MU_ zvh?Lkt-0Isq)2GemTDv-IJ@y9Za2*EeJu*Emkb6*@>4N@DlN6RJU$Fs<%Go1%EG26 z<&WE@#CI%$zv@%IVCgz85~0I@h48b?) ve^;3dtR67@eI`Ah8a@b* ZK$Dm$zwln5*5nDnJ0=v!Gv`Av&a(iA#llp+%*eIhvF4ifh)=GSN|I!6W9 zqJCB=8&+Fp@a=D`*soaK+q#}qbY*UinYgERLHlb0#zR6zR>w{;S&qj7%_I^JKS7=^ z<{A(eN{32)ne9IiT$h*N=hZ*jzv~N2H-V2ltZ%MvayAWq2WO=NwbJFZl{}dyo#JOJ zXzY#|L#zO-v`Ic}_lD<3QtpvmueZ3^+h?;>uX+6r89!$5u)*`vsc)rPjYV#gr$P)t zdA+&B0)Cf2AwE@(YY&N9P*$$)S`EzI%Ww9p=tqRRpB<0=RIC4JmVh;2xxNF_$gciS zv}7~sRh>?ITNaI}YBDpL`>c*K=ICAHOr2cqRdeGtvL9hQtg(hw|3 3ZrKPcAZ9|xX)&4)0JpSldR3Us-T z%c{4DVA^jdX)=Fcz{K+&>ps%{ zU6Js`iROOsO)mlM8(v*$kELA!-GojRBEvp61uo^3ZZLyjXETxv7@ek^cS%(OXQvn9 z6?QS22TkOTPt^)3hC*2}ho)nt&n_)OLl#4{jH6cI$qq5XUSIFR)AJ|1%P#x?j`RU9 z)Z@urmX 6Ck=d} zK7h#p>Z?LsYut<%qB-$w$hpu*hR;8px!iz9>pC#K-xI7vnMXAZq<-w^ddn2gBk`&g zXWyZRZB1gGxg9-=Lc`C9w6QF2qe%TYbMHv}G_gT*2Z`)U&;5C*nqAxlRk57Kk|LW; z5HQ-N0j}Jh#V`PUxEgi>-=6ZrVRws~2gg@yYKjV`a{_P1k gp{0^RsC$-*}L+rSB4TkXZrpttP@KygWYg2@eo{3henB zf*N4FB|t-%r@mk9;P@HP4l$)HWWO$u0ybPp*lQPHY^g|uhn3?()QI=^6uqmvvcr7_ zDSaBtnu=DSir=`d5~DxSDsWyiT*gR8F?`-WC+GZnDpv>h0=nA1hVbpYkP^(^3pv7^ zYWWZ%x$~7?3?z2Q-Y?cV{ur)Eb4A^>Q3-{$Z!_~;I7~tmq!bTI2=kzrG?c2GgA$$` z#}L0z?{AXnvbGKaYPX}W>GW$A3UUmdO{#e56|#^qn6(BbbkVp6Ys00SxhJ@$ >hIa}}{(KIJu-!xIgM!Wp7Z0h3njXCAJP>~ZX^FQ5Q(3ui=I z;M4D3PN^Pm6}<3_C&&rRo91bk#$VrmdV^zX_2MDAT{bdHa{C1LjLLmKZ-5o)ZDGb> zi3;?ZH%X=CbA%UC%%tcQGdCGzulKQ#o;Nz+#yyYn)6~Fduyiz9qt>-~LTBj&sEW+U zK@Nnx&1#}RqH1K89?{&>_?&5R=7M`4|5b8@hiXXjAts-nOi@#Yz+8JR+v4_qaAlU1 zv^J)wZW# +ZY_K6 s`%&IKThf3UWOFo>ob|C`CQe-GXW7;4fl^d>l6G@6 zRwh9oAcb)}oTk#O#}V{I3nAY i5ij^xlH&}rIuGDl^BvHjnw>g7E9kPk_oyd z(;8f0_stWJceH1rQAoCOwZZRmwE1HMOgN;LC+AJ#J-Z6}3w9Po`6h+x(^OmY7+VZV zoB{szMf;Lh$I&6D9%FKDE#Ss%;TA%*_)zqb`d^xm@|D2P7>XM|G7o2pEgwkP**_y_ zLpE_9dOL^K3Lh*Y?t>)B78X+~Ai*FYlJ1wolx>1n^vs)#%R9Fj&g7g<;Zve`%xhAC z<=pLE`WuF}myF}cH(;NEQQMCLwPYEOsn1v{@(7PzUEjFv3~=-Ft!eHke`UB&>v-9* znz&O(*NUGt)d`~`q6!g4ZXgtO|LD0FT;FqVjBUspg2_{v6zIkmVKWU;^L%n#qduO9 zf)$V0qaoxy2cMx`nQ%+R_w{)96Y$@Ko}p~jd%sPI%^i({Tz-{LNW3X@$K3`leojR6 za{OhMPh!H+{+VK}A+Op6e6D!1#6+6!#KftQI*->=IS2iFaJ#3G+tu8!gw*#=-WoMO zTw{if`;Fq{;XlcrTi4XS 1etvr+mA%Prp4_H+eStF#9|g|V}G3Z*fLH?Dft(YM2YfB2{xak3*{z>%Dr zRE4nEESS4xeqpEN?0swCJV|^zxs2zV(y#W6C WCCwg{a;4g4|z;k`Sx5=7HqA&wBBK=L)0oVzo`Xs}8e$p?GyZunkqI z{D$hym(kAiY&tDM$)0nDUg?uXMFQc)W$8?31gyS=b@mk@$hOnTw3^XKB0u714;f#3 z3~CxGj~!R;GRtFl@+7`O+)o&=CK6$Pb)|KiC{m_vbixyB6GDULy ^SE3y=Up;ZESsNs7 Dc8{?#sn(myDL}`OJ>?`$uxU zF;M8SIFm_x deiXzyB4Ek($5^BfsuVpDhR zTd|#5%dxb)%AntqQq+WpIhZl9#gB#eC=Vi1(gI)^50w?N?J6Q@Y7c7b_F+zRCEOkc zTpMXs9fa`B=zks7Y0|VfKF*0&(lU6|Gmjj>nV|)*AuyL5TS3zEQ9gmm826xFweDc9 znmvHa-OwOfT|X+m!c6^4P_e4B5=+X9 Xo)Z($4dVp3xGd?SZ3g&5DP>fZ+P|j{F7WL`$ZMnS+BX0!BNdg%_Vl z ;T5Hs -88B7=(dqh#u2yTIQ|n3GBxej-%-Du*(5t6rAIN5|LgLzA&U}Kmi!uxsFV;vc zts8VvzcL=wBqzk?%fofkX@O>04AW0CwNq$BGf$1{XClzI=TzeqpoHA9m!*M+kD4{X z86(?79Pfn0fny`d%=gOj!8YyyaWc%lJE4jJzVvB8d-}h6>A%14-_7Vh+!GY{Wxu)Q zPE}|p)Rk^Zp~-a7>?c&C9is~Ch6hJ`!$A2x1US-5)wjGq&^?oP2Y-+xi85Y?0EVOE znnKUE0KWt3ccpV)X4n~RU1kz?JW{H9;Y~|rqA!i09w+j=Bqs;(G{M&e0d}}s>5aOa z&C4`xr6c|>p#U<(6!@k(p0ClG43{Y%4AGvyvI{2OXdoT998`DYrqlo=QD0 q3jr4q9=Gx5)oTuJPU(;hA>yD7|^YnVFb;d7?>^7 z@*#zgt<10!x7=bzd!YEb4C4|frw^CF-%;*)I3?IzK7N~Zcl`)o#VN))Hg&=cj52A* z2CZ}Nqkvh=Nf+MC;xv) @WT(+b z0&)zCCNXDJ=dPlos>W!RG+_7CNYtu{s^7xChI9$g>qq6jE!+bC)`OwpEaMfYRf9!^ zfc-80jznLIN%8e*Bk@Z<2B{8q0n|L3m_$^?e0F-fp0h1cs`-2>iO*3kVY%wO!oz7i zkGZ18 ?ptEKy}Hc#=f`fc!|GK_n2DRIaL3l@SF+@qK)%{|3v5_tu; z;3&KV=Xe8Xy1|B7H6pS+`g%LUyI65oWS<)d(?6qf*-39^Mx@9PT%5{L{*w2ebZ1Ft z1MT?|7_Oi>FO*djH_FpKx-kM$n{h}Z6idg5w0HbnO56~TQ?^juE#crX0U=zC3q4`I z4y<}cNisu|Fz>`Qd;MEu9xokuX!!+b)^^K*ZUf#oiI7$S!#WZJm2kO!AJ>1%t^AvP zz%rF3Ra_CY2bpR>PxU70Ga~xDIB+O&2~9}TY-3m@-JZaF-&*t*GAT72$BlyksE)mX zmcxvVmWrF05CR^Li^HQsxkK{=wxRxKn@MS`PFthtTphP9tWFnW9bWeko#^hgYw#!} z%kkr(3#}58YW*Sp%B9C^ZmpN?L2>>nWMqq7&?q0OCIS$v<^oz82U2eZ+{wBE%ZPX_ zg%Amje7aLD4#?<9dh%#=#e@^G4OFJs-6FaOv9*nmw!*`P$D^n19r7wW%2J%-_VG*v zB}mIYeLz>-&{L)U*pkx6l+O0foX(6UISof@yH?G8p<0IAq9R%J6}yE@-B$R$hsc<9 z!)bsgzvN6BU}v~^Ks2z#MU&M&U@CgzQ-*C;uWd+1c%Hf9RE_@pXJ&0V3z3l`?0|MC zJC*rqt;jMHp_Peh$XFsovAr%80Zm2eP-`0!jT6~Sz)|0YzD1)Z&scL 5`GR#VbrOqpV52WbZ!zDnt#Xn0}bYQ{Lccaf1&FVQy5HoE74 zpnV;Z=wg@mtSNr?o!8Q>XoJUvu>6Z)acoG|vgJwVX9KAv^r(x8VKqB?JEfPjdPtZC z@M}3wWcsl#gt_CQd)mVQyl;E5+^*pdV|4dX43S+d?}MKx20&^G3-p5#1XB$^?=4{A z6-u)lM8MA6fP)&mLWwkRe^cRd8XiW)z?@c0EZA>=)D=rflifQYmD_2iZGg^E^TtED zfwmw6uqqNCiCN&bTpRvO@p0zH-4%J16Z&T?jHT;ram9!Q6J?p}%#gd6NIaK8fO_ne z42O7p(Uh88XrEWl3)WE(vs0jni`4HJ>Ss-{d{Z<{ir7BT6&80AS0oIx95>`(SZv3x zGVq) cm~_2|&-J+Gx-pO$x*udd8E9;oTvWrM@J(SHhgpQC1R|-S*e(8Okyh zYO&$-l1HN_Yv||viZU-56=@v;*qlV3$8H}Wad?51(e0$YgD#j(ZZ8Ab?L#<@o(I@@ zQgKyvOgcI_Ncc3)x>@xJv_CYt|5A~cXRxikC$u)7Fl9Yp-Nu*1JzY0U(LNPz&STPg z+RoqD*Iw1+8GnM+xK~WhVYh`eV1CV{q aIz-?PWE)akA%OuAAX ? ?6COC5zag9afkN zmv7uB+Iqd6fSx^yS+o4{*DM2@=gKV;Ix5-<7Hm8KO(g~sYb!+@Ii@7Wv_xS7StQ{! z9v}OZG#<>>PK}hA&$JN}rq&~hOmTdXF%hw)2t!|`UfKpWW$T(w;6fG^FWH*jHAUfL zwRG Q6HH10W#^OjYp$_oaq#ZlY(b@oQiyWJ}6ZDL9hU&yxmbmp?R)i3EAGsWvpF z)|fLk^jtVcsNZrvu+R+(D$)>`IzNu##_5)DDW72BnP7AAwujN^)@$Zol4XxO7Vc4` zHokc1cL^}ib4#2p3``u22sj9$U50GaEQk-F6{}k2ydjFW7c )qBJ-e4($AxC9ZgJqQC7Dc5$8Cz0=b zFN(BnlGuDri **uR1u D^Stmo~_3CLKIdZ>fh-l z_Fq2@pR4cUB#eK6`+G0&KP8Dy2KItt;Ni`GrS0&&8wfZ7Yz@Be)Bi==p-g$-5=9<~ z2a^hJM5VI+N}kU&kKWvbI!WU;yZ+e+HsYDbLJAMsI(=m$ eS2OXue BwzeV_tMrhj2vN`z; z*baeKft>hRib?9Sc0k~qj&r4Z`7qL)J uz#H+i?oq>#}v5$dmbc3tZi)^>Q;LOU}xXcRCa|6-5|cnyiDU%ftZyI?d&c z3g@nM#~Qg~e-jQ>yO-GAT5JYA9uhdgdWX+1fIb>(HJ8v6kWTKcGIi^ og|o7`_ff4&CF9gIA4Uv%UR)1MK`)| zUmnpk%=f=eD4KscQy|N_ecrs*oZJW5+^9&|7-B2&`Yw^yZL((WK#_}QR-cK1MZiit zd^w#sY#1F{fx77o4Te >FpD*6ElIjIHM`KIrtd5 zQ09<4YB6?wM(}jXB4m $ ze;@h(;x+~1`4%3dE!D$>7Vy%rq4}D<<5;6tWnwQJ^E1|<92zVv{j!k-Jhlam`inb@ zS62{s^12Cv)C_(X`9m(J?Ju8F@^!pkd1MVY@C@&xbx>!79WSLIiFKMz$o5Am=7QxK zGVqrQ>n(NbBl?oMyQDij?){&Hs?)~Ip9_v7U~j5n_!2d;^ue3e$-(v)@trJDDX2q` zIwqU84$9~y>BhHmQ4T_$7j{ZWwmvslro~{52|K0h;fBF_J#prO`c{;i{rOyFS16Rc zvY9sfh?D3!)@VW)d*~=q`)z(cZy40bYP8KB;8jo(ceT=H=Jb01CMwCg>zbf-bBY$e z$oNAfzjf2RGox`dGDAt?tblhGWN3v2u^NQb{=BJCkt?)$ooQFIN;6bmK|F$Abx}OM zUv*g={?2Cy^|?1Z_bZ_0B{njIl|5kfyTkoUO*cUULn~VYQ4?D`dqoG`A5HXqGVW8c zPy~kR@U*P1Y*c5<4Up6{&lMnI^v` &sFGCJmFwd6QxX)+TxqO6d=9D79!JOWkEm*w}mxxFtm0UgX3=P4gHd+BA=KGnwO%YgT<(*VE)cKz70sU97?l z#jrT3W7l>ryerH#4Uw0LH<#ilZn7K+<2Ywn>FugeQ)bXk yxNmulATbnA+HsuC@+XL!6a2~_8?;9d-j#IhCZXy;F!`tT*5 zu12nrFOq2`-7NSbuE;%<5#2-oeHf2@#FhNbqxlZFeBu0k7*}+$vp2B#H-?P#m=Vby zezbtGk0lm$^&d=UL>AcvQH6Vfk)bd!6&U4qJyQs#qRCJxrUaKIRej-rixm_~6Qm-6 zUiCEA)3Jy5X-%zOt?gi)VCIQLh~XsWXxZxi@4?N{vqXrzhPf%mVD#s2qWJ4a2E?=G zjR=;dmyko;Uf9Md9NLAx5Ta3IlkF`{tMu-a&xBj3g~aDF)_BSa4Ir9mLtPXj#)i^9 zXr9Iu3w3PbP$UUK1!u=K(1o?~7-|&Dn)q-VX)h`h=vvPi_0 zz)<;EN`(&LN=eOmC@18+BhFA5QYq_^2zaho7GAI1fGV2L2AHHpxv^(;SwA5gr@+3c zb6MR)B|_PhU61j_I|QTY Y$C!oyHfICSLdEiXlm2C9{^8L4+Y&ThvCDFbAIW3mxsA0# zihpgKFHx-znGCL?B~=gvvbeZ>DB84;74CqoHm6yw4e}i-CIgYpD#R64Z)ti$Z7`bd zP)71nGRNV5sn^5xHC!h#9 yR+WnO`6c9Jyr@|QEx zO|Ev7x8rt;OxCUw&UBx|oy0%yv2JP$n$Wew^F%1Q-!VTWk@#qQG1f=9<}Y|NN~Gd* z)>5V@$eW~ B03#k~!{9`1X_nUHo+B_B;r>uhaUez!cM9p&Lm(RxiR? lL-(jHU^XQ$mPkzxxy9(_Qtzbh|j%lN~a?}Q!>mjjNBUw*vU;AA)yA^VXcWf>S+KgD2 VIFe@!ljLF5C!-Jx-Y|Z|QB=3< zo5ALtqvciS7Nn Y&=A6C>{q V>ejkNv5*%dg zLl bZlxHLj=m~;o0 z6(w6z20UUh^|{gVwk07~uPZYX>Z~1FtoUIrEZTXxmy|dFw|RI%u<_a*}7N4w>c_ zM}DobrjoVV>OBdYjuY)3*WWRbVlJDG$ir^-ek@ed!=)NMNm8onF(k8DMzW2@Lt8)( zw0u@6NO(DnNmUL(H|QF=tV J~qc8zUFBy)W$1! zLT(qnW*J _y&j$Sz$K_Udl*cmOSmyD|EaRySHcspah9iojFsO zvV*?J%49gteHh9PO+kz`1V@t|oKlN2V-h38in=Uvc~yQ|O134 zO01op`%C7TDdu=Z_>7L1g^MMs@_i)MVTGe{(xUIT@82R9PiEubyj|ISr!6%y1ULLN z6Q|(ubeH;!?hZWxO3}FtyB>ZzJqC F6xmo~g*KhfkMc zQ#WlgX8!Vnr1lPX;ZA;sJ6s!d(%1krj*VsGmHS(lCD|72`IcZwpH-tq@UC&NMM9H9 zLFlsgS_51112IR{koGa9vG)3rC5=x%;>l3NFfZmT;7ubMdj}eGAHkF}9{IQDjJ_c( zd=38~yg=+ S`Q@z{(bjW; zZRhAYtv7}e*!tOB2HDAvBVXANS`v!w(l#dLj9*D~MI@*9YK kWo;u_pN%TJku1?FWcf=t?$xTAL@rhyhq zfVmY+!Ltt1C!MG^2fv+H5eD9b!25@0Kk&%+|A$^boBZ#3{hNPN=?4Qxg8)X(e)8l| z6Y%g1koP}2^}w;=$A$E0U*OxXG9pR>^b)e7zys@lPsSF5x%kacq71yzzWwKqE7#}S zr{sT<$q2|wh>9pGG02F5B792#oa6rQ0?u 3v724&p-qGneNq*(yo zBtPkZ&s-lzU>^QZ1c{$z`2KkbinkyxLIVl3mdOEI;@c+i&nwqQ2WUzD10GPK*4)7k zc-%_V#N0qYPtU;4P6l8JF!~w50F*$l!lzjWSVc%c68fL$z-O*cJ&^Jb1gf?GYv8Fp zps~Wx#K-}t_6(Fg|8w5uJ@8F7fu+U P@)Ek6_W-~jza zY=Br4e?yN0zI=bk_}?O<{W-^1OB=yQK)iG!pr!tAm?t=Y!~`C1vj>=){~2wmQ#!a8 zSpIlmhyQ!k64Cq-P0-59{#yVFYZ-uvCD79OE9?Ylt96MG4GRIQwGQLEBLC04=K3%) z{t@~|tBE)p=sEmHpBoevN$T~NeIP0=Fp>x`hyS^9eHwwEJb$QCF 0wqroa08083~6S807vDo`!+UsUlr|3vj)l+8im zK}ETL!PDyg6a0^s_$P^OP%2P?&tFuMrvHNKd!_xa>H?KV{6%78@h?dJSu7E>M4)PN zzsM@A|B39cd%&QWpjt-1FyA=-6XuTw`40X4kH2iGfwF*#=KNyub^eXz|ImJy&;i8+ zl?M2QdF1sw=D!yT0ObKauKtUs*XK8$-v^I>b$A_=6ZF*aFHRf(|IPX5;R@6@_7^5$ z!vDr3``uF(G}EAdh`$KXlK%z4Uq`7wmjKiY>=(gh>Td);XNiAfN(C(msFTGnBCzb= zh< NfwMsQ$-X3c3gO3)ZvnpJ0Cs z82_*%23i16OZhLR?$Y0wK$rY~sk{GCAW-Y=FPixB-)Q~<`=dbLAOA4+ftvDu;qO%b u3;h3X*aNLEQ2WF$BKO+=EDZmfwL(tn+t=ZvM~uK9W28rq9)L+6J^Ft^O!7Ve literal 0 HcmV?d00001 diff --git a/jobqueue/gradle/wrapper/gradle-wrapper.properties b/jobqueue/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..59739c3 --- /dev/null +++ b/jobqueue/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jan 30 14:19:18 PST 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip diff --git a/jobqueue/gradlew b/jobqueue/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/jobqueue/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/jobqueue/gradlew.bat b/jobqueue/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/jobqueue/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From efd79cf255e347fbfc9b9feefca78b546a7b95bd Mon Sep 17 00:00:00 2001 From: yigit Date: Thu, 30 Jan 2014 16:52:14 -0800 Subject: [PATCH 17/41] update readme for 1.1 release --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a8027b0..bb5f90a 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ At Path, we use [greenrobot's EventBus](https://github.com/greenrobot/EventBus); ### Getting Started We distribute artifacts through maven central repository. -Gradle: `compile 'com.path:android-priority-jobqueue:1.0'` +Gradle: `compile 'com.path:android-priority-jobqueue:1.1'` Maven: @@ -137,7 +137,7 @@ Maven: ``` @@ -150,7 +150,9 @@ We highly recommend checking how you can configure job manager and individual jo * [Review sample configuration][7] ### Version History - + - 1.1 (Jan 30, 2014) + - Job Status query API (#18) + - Fixed a stackoverflow bug when network status changes after a long time. (#21) - 1.0 (Jan 14, 2014): - Added [parameterized][12] constructor for Job for more readable code. - Deprecated `BaseJob` in favor of a more complete `Job` class. From 9e29aa7ca4439c5b121ff17fc3deb9dedde76da0 Mon Sep 17 00:00:00 2001 From: yigit com.path android-priority-jobqueue -1.0 +1.1 Date: Sat, 8 Feb 2014 02:46:09 -0800 Subject: [PATCH 18/41] fix race condition which may cause running same group jobs in parallel #35 --- jobqueue/AndroidManifest.xml | 2 +- .../com/path/android/jobqueue/JobManager.java | 48 +++++++------ .../test/jobmanager/GroupingTest.java | 68 +++++++++++++++++++ 3 files changed, 97 insertions(+), 21 deletions(-) diff --git a/jobqueue/AndroidManifest.xml b/jobqueue/AndroidManifest.xml index 29eff0d..e2fd7b0 100644 --- a/jobqueue/AndroidManifest.xml +++ b/jobqueue/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="1.1.1"> diff --git a/jobqueue/src/com/path/android/jobqueue/JobManager.java b/jobqueue/src/com/path/android/jobqueue/JobManager.java index 4c795a9..16e0f02 100644 --- a/jobqueue/src/com/path/android/jobqueue/JobManager.java +++ b/jobqueue/src/com/path/android/jobqueue/JobManager.java @@ -11,6 +11,7 @@ import com.path.android.jobqueue.nonPersistentQueue.NonPersistentPriorityQueue; import com.path.android.jobqueue.persistentQueue.sqlite.SqliteJobQueue; +import java.util.Collection; import java.util.concurrent.*; /** @@ -40,6 +41,7 @@ public class JobManager implements NetworkEventProvider.Listener { private final ConcurrentHashMappersistentOnAddedLocks; private final ConcurrentHashMap nonPersistentOnAddedLocks; private final ScheduledExecutorService timedExecutor; + private final Object getNextJobLock = new Object(); /** * Default constructor that will create a JobManager with 1 {@link SqliteJobQueue} and 1 {@link NonPersistentPriorityQueue} @@ -260,30 +262,36 @@ private JobHolder getNextJob() { boolean haveNetwork = hasNetwork(); JobHolder jobHolder; boolean persistent = false; - synchronized (nonPersistentJobQueue) { - jobHolder = nonPersistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobGroups.getSafe()); - } - if (jobHolder == null) { - //go to disk, there aren't any non-persistent jobs - synchronized (persistentJobQueue) { - jobHolder = persistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobGroups.getSafe()); - persistent = true; + synchronized (getNextJobLock) { + final Collection runningJobIds = runningJobGroups.getSafe(); + synchronized (nonPersistentJobQueue) { + jobHolder = nonPersistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobIds); } - } - if(jobHolder != null) { - //wait for onAdded locks - if(persistent) { - waitForOnAddedLock(persistentOnAddedLocks, jobHolder.getId()); - } else { - waitForOnAddedLock(nonPersistentOnAddedLocks, jobHolder.getId()); + if (jobHolder == null) { + //go to disk, there aren't any non-persistent jobs + synchronized (persistentJobQueue) { + jobHolder = persistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobIds); + persistent = true; + } + } + if(jobHolder == null) { + return null; + } + if(persistent && dependencyInjector != null) { + dependencyInjector.inject(jobHolder.getBaseJob()); + } + if(jobHolder.getGroupId() != null) { + runningJobGroups.add(jobHolder.getGroupId()); } } - if(persistent && jobHolder != null && dependencyInjector != null) { - dependencyInjector.inject(jobHolder.getBaseJob()); - } - if(jobHolder != null && jobHolder.getGroupId() != null) { - runningJobGroups.add(jobHolder.getGroupId()); + + //wait for onAdded locks. wait for locks after job is selected so that we minimize the lock + if(persistent) { + waitForOnAddedLock(persistentOnAddedLocks, jobHolder.getId()); + } else { + waitForOnAddedLock(nonPersistentOnAddedLocks, jobHolder.getId()); } + return jobHolder; } diff --git a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java index 00103e9..0739d3c 100644 --- a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java +++ b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java @@ -3,6 +3,7 @@ import com.path.android.jobqueue.JobHolder; import com.path.android.jobqueue.JobManager; import com.path.android.jobqueue.Params; +import com.path.android.jobqueue.config.Configuration; import com.path.android.jobqueue.test.jobs.DummyJob; import org.fest.reflect.method.*; import static org.hamcrest.CoreMatchers.*; @@ -11,6 +12,9 @@ import org.junit.runner.RunWith; import org.robolectric.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + @RunWith(RobolectricTestRunner.class) public class GroupingTest extends JobManagerTestBase { @Test @@ -35,4 +39,68 @@ public void testGrouping() throws Exception { removeJobMethod.invoke(group2Job); MatcherAssert.assertThat("even after group2 job is complete, no jobs should be returned since we only have group1 jobs left", nextJobMethod.invoke(), is(nullValue())); } + + @Test + public void testGroupingRaceCondition() throws Exception { + DummyNetworkUtilWithConnectivityEventSupport dummyNetworkUtil = new DummyNetworkUtilWithConnectivityEventSupport(); + JobManager jobManager = createJobManager(new Configuration.Builder(Robolectric.application) + .minConsumerCount(5).maxConsumerCount(10) + .networkUtil(dummyNetworkUtil)); + dummyNetworkUtil.setHasNetwork(false, true); + //add a bunch of network requring jobs + final String GROUP_ID = "shared_group_id"; + final int AFTER_ADDED_JOBS_COUNT = 5; + final int NOT_SET_JOB_ID = -1; + final AtomicInteger firstRunJob = new AtomicInteger(NOT_SET_JOB_ID); + final int FIRST_JOB_ID = -10; + final CountDownLatch onAddedCalled = new CountDownLatch(1); + final CountDownLatch remainingJobsOnAddedCalled = new CountDownLatch(AFTER_ADDED_JOBS_COUNT); + jobManager.addJobInBackground(new DummyJob(new Params(10).requireNetwork().groupBy(GROUP_ID)) { + @Override + public void onAdded() { + super.onAdded(); + onAddedCalled.countDown(); + try { + //wait until all other jobs are added + remainingJobsOnAddedCalled.await(); + //wait a bit after all are added, + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } + + @Override + public void onRun() throws Throwable { + super.onRun(); + firstRunJob.compareAndSet(NOT_SET_JOB_ID, FIRST_JOB_ID); + } + }); + //ensure first jobs on added is called + onAddedCalled.await(); + for(int i = 0; i < AFTER_ADDED_JOBS_COUNT; i ++) { + final int finalI = i; + jobManager.addJob(new DummyJob(new Params(5).groupBy(GROUP_ID).requireNetwork()) { + final int id = finalI + 1; + + @Override + public void onAdded() { + super.onAdded(); + remainingJobsOnAddedCalled.countDown(); + } + + @Override + public void onRun() throws Throwable { + super.onRun(); + firstRunJob.compareAndSet(NOT_SET_JOB_ID, id); + } + }); + } + dummyNetworkUtil.setHasNetwork(true, true); + //wait until all jobs are completed + while(firstRunJob.get() == NOT_SET_JOB_ID) { + Thread.sleep(100); + } + MatcherAssert.assertThat("highest priority job should run if it is added before others", firstRunJob.get(), is(FIRST_JOB_ID)); + + } } From 94716f8a2672dfc5063bb3e52489b4a73835f642 Mon Sep 17 00:00:00 2001 From: Yigit Boyar Date: Sat, 8 Feb 2014 03:06:08 -0800 Subject: [PATCH 19/41] update readme for 1.1.1 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb5f90a..40f319d 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ At Path, we use [greenrobot's EventBus](https://github.com/greenrobot/EventBus); ### Getting Started We distribute artifacts through maven central repository. -Gradle: `compile 'com.path:android-priority-jobqueue:1.1'` +Gradle: `compile 'com.path:android-priority-jobqueue:1.1.1'` Maven: @@ -137,7 +137,7 @@ Maven: