From 2d9906190221ff46ed796b6526e0ce1631623828 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:31:41 +0100 Subject: [PATCH 01/12] Issue #469: Implementation of Event-based Asynchronous pattern --- event-asynchronous/README.md | 28 +++ event-asynchronous/etc/event-asynchronous.png | Bin 0 -> 31413 bytes .../etc/event-asynchronous.ucls | 110 +++++++++++ event-asynchronous/pom.xml | 42 ++++ .../com/iluwatar/event/asynchronous/App.java | 185 ++++++++++++++++++ .../iluwatar/event/asynchronous/Event.java | 88 +++++++++ .../EventDoesNotExistException.java | 26 +++ .../event/asynchronous/EventManager.java | 148 ++++++++++++++ .../iluwatar/event/asynchronous/IEvent.java | 27 +++ .../InvalidOperationException.java | 27 +++ .../LongRunningEventException.java | 26 +++ .../MaxNumOfEventsAllowedException.java | 26 +++ .../asynchronous/ThreadCompleteListener.java | 21 ++ .../src/main/java/config.properties | 1 + .../iluwatar/event/asynchronous/AppTest.java | 32 +++ .../asynchronous/EventAsynchronousTest.java | 73 +++++++ 16 files changed, 860 insertions(+) create mode 100644 event-asynchronous/README.md create mode 100644 event-asynchronous/etc/event-asynchronous.png create mode 100644 event-asynchronous/etc/event-asynchronous.ucls create mode 100644 event-asynchronous/pom.xml create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java create mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md new file mode 100644 index 000000000..59e6e8b33 --- /dev/null +++ b/event-asynchronous/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Event-based Asynchronous +folder: event-asynchronous +permalink: /patterns/event-asynchronous/ +categories: Other +tags: + - Java +--- + +## Intent +The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many +of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- +(1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without interrupting your application. +(2) Execute multiple operations simultaneously, receiving notifications when each completes. +(3) Wait for resources to become available without stopping ("hanging") your application. +(4) Communicate with pending asynchronous operations using the familiar events-and-delegates model. + +![alt text](./etc/event-asynchronous.png "Event-based Asynchronous") + +## Applicability +Use the Event-based Asynchronous pattern(s) when + +* Time-consuming tasks are needed to run in the background without disrupting the current application. + +## Credits + +* [Event-based Asynchronous Pattern Overview](https://msdn.microsoft.com/en-us/library/wewwczdw%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) diff --git a/event-asynchronous/etc/event-asynchronous.png b/event-asynchronous/etc/event-asynchronous.png new file mode 100644 index 0000000000000000000000000000000000000000..a46ffe1c5d17773d38dac792349cc547adc06955 GIT binary patch literal 31413 zcmb4rbzD^4_BSSqlz?Dx&*z+d)?Rzn{;sw6nuyoRG8kyYXecNs7;>^wYA7i8Mo>`h3_ZFFJh_SW z3_wBg^_P>9(D2UOZuKkEpAJ|QZg6~eoe+G3H}b&tEo1PD)&>{ve4cRSClT-Ud=w>~ zpT9hSNGn~y<8&Y2)J8==dXb};TZkJ+f`?1%W zwoGu~s*owZ zJJ#-y-3312C|o?GX$YjQ*AY9wQ6NEKc`v!nIgaH)OVj6Y-{nl+>|2Rdg8a*r|d4ZS80uMyG>s` zkm@baVTEo~x79dM5p58@D(6s|Lo-&Oe!yR=5V83FSJ&v6uLdF3YJQDqjk6aDI=}5_ zU&n?3!LP5z*l3sQ6FSpjL!Z5C@Ewm?JUbk&19QVja_>#7h*SlZ&}Y49FG4NE%EqgG z1L|0D_Dh#d3!yvwz(sA?5-tWMyVK$D-sjyMCAt@tOs#Al?4&epWT|KVB^P@ADvM1@ zd;w}fHc3Fk9if-YnLXM>=JD6jDdlcBAD7-*%9)v$Xx?VLI8V2!9ZkI?kQND@JjqK9`q6bT(ylqc9wp1qW{Z+;ZvHWF((-13494@A<43z%XUHV%Xh zu-&8L8E*F1J)tn^9Vz(T*OZ~1{|C}{y2PFx-mR7YoAg+B+}Thw!LE1U4eM}Oz*%*( zXLf_SeUxaek){EX6_O~?m+I(VwdV_ZK>-Iddf--1$Hgq8#h~fwuDTiZ3r$`bB#UF0 zaU~mj%)^gz0v1y!ywA3Oyf(c6b<{^*)f(xsA_;9pioVR_fF^B31)Yh)swrA;+V^|F zxbnbUra|!ODQu(wWIH53(ZRk1?d->r`>u{wfG-Zb0yzS=WP+%!+no0sdyIT8i^-ct zkD#OK+sfkEbO7OIfN%r{bW*lq(@*@`NbKe;=%TMWWhiiUSTe$)Qf50v)QbZ&y({p{ zD>DAua4~mCx8bX;W;Srmo$tXsf;6_i>+?|**V|tg1uyn87H{fK7!7ZBE3Vevw)jcg zG~s&jWw6QLsdc$X6?`&{jblOgdT{>Z;$~|HG9=O{c0Kpo01>=+Qw@IR7q~lG>8(}p z7`GJE+}BiafS#f|hhi*2OoF2naL^dw1>V~#X1`YTUR}J7It1UWjaRgsdQ@JeXwl2C zi%>3HO%hNjW9=8ykf66t=5Z|sQ;3L~Q5w2g=}F0EhTuQ*3pF=sU>@l;a}xEvK@59W zJQ2OQ2qHFwdl}_~`h{ZNoY>_r?}kPkqI+rgNYyHEtW7%|Dr?zIGi9qzNRyjWVX!&c zCrKi$BnBg*$WbhC9ZupUb~8oLWpVRsH?ZO4vLfufykh6#m_VHMD8gzN-zNUZ#T)&T z5wuh)eThFhGVporPTAEV4o-?p6d?F24(PMrKbnWHy6Laa-U$zZ&?-)9D}u!R5gDaH zEHq=G5pH@7A!yXw+Bk&2o!b#5Xc4E1NkVk{t2NH|j-t9jQtE(^Js`Ua{+CMNunU#I zv!QVED=hKDpI`zU5`NP28FtcHpXEx3qkwx9&f|MItl_mhgu2uP#58)8fc5j_&2Z0R zrb`m;pt%o!<<2>udE&KwxkN8^-tyYJ)(U)S1yiWJNuUr7ICU_>z+FFreXwd)Om|qC zWy^1y-^e`t(QHC%UXhm0XiM-G&Bk7q$K!Bc_y#@-As6%0&_6LUxT4?}^|^?V@p{~P zHZ+^U-dbgKq(>pzM(+Si&N-HY5IUyv<=!%(0x)s(Ykqyo@nVy#-hV%#WBes-GW)8} z&s3NPnlp!Z*@(YW;`#VZ9Gbu9-WaJi<0L1c1$Pe z#8%LL)}l!DHG=>0;cigF$+EC?PFsK({JMfHKDYn`MSr>y0o!3d#=kg7fD5D&{P>|L z)_0{@Q4m=Dn7dq0=b{V;;D<-(dZ-nAS?lK+U;(cq*u9K3La#W1ril|kOk+nDAj2&U z`>khSk!fd{hrSlpM}(^V7$B0|T^>G~bfjanfAGItnFqU_jGW%=g!jsvWl-D<7aQG# zhz{>O+wI0$4av1Ciy`palBlw+frjfrbS~CuY1m?kqplPa;4DFg_X)TWJU^Y8b9~nn zY=b4*owaho=cD46#YRF`-?Oa5{a!ad248f?c4lAMt#>xBS~1E^zt$WFA}%KsrM0M% za`cPjqQHKhjAI+(5L|WA?j&*$L#?9kt$XSej?B!2X>GD&3RFsW%~F zaU_#4Jf#QtNcxM90K@wbMPT#V8dYAcRB<0GYo-fmD2Ucjw!EX}D8x>tK*zt%Jk=%g z>P?@q9yyJJf1GSOtj^z0$0+y!J{bV!zZ!CKg}a`oiMN`qr(j*r@#pxiDCwLAZN{E1 zT=VWb#&bE5?|jSbs*TcZVe!g0e6f5@&n;^Hx%96nqf4uQJS^sW2q!t+zRquL&Y5Sv zj&!J`_kc83@zwEGJaByY6lXoR+f;nqT)01*fYOhSqWqPLukzQ-iS!_6+Vk&^MGI@x zFA6rg@B;3$7VcP8WzaqA$XcXgq=1Ct%b|N&;CwwF(rfcK-sQ*K7A4psF^%1>^JBX; zYj4?_%`7kWz{`nZFOkOel)&ADrQKp_ZpGcVFpy1oS71@EXR-+U%OORvbFz<6>P=8G z_{i*qIA0GHxSUVHE_=Zf%}>4*R<|%$1sg(~9GP)Kt4A;AR{fl=jw#5mcCJcqJm7T| zk>KL&CWe!VK+VCY$SsOToaU21a$O7fJAjq4co~L9r}tbXDEbKaM750>>%PZwg6Y0zT&X&s{O*51T^lFQ))Y*QuFW%&qr2;ozu62%gHz z*E_FgQ!P3}#D79&S)oOV{{w|&V4;5~_&%u=+UfTk%e<_H%+&_!uliZ)sTT=%B-;$8 zmr@i+Q8->y67J0A{s;U7R$^-8S(Q~d6vd%Wgucy<_=)SzpwtE9A={U$CQhohWE@pX zzehiYmEo7mYvDWCp*hT_5K%tZyv;(pQHhbNdAB158u2fP>w;;^e)2@QPCa(6j!uI< z?7uc~C5DqI#kysE7q{K^E&Wdq{`Gz`taEVG*@N^d>uRre?chzar}rlx@$0+5PJ2fg zrB>sL2f)})>dh!MSzoex{5gN<(CC~*Jc@v$bj0X>2_)WmJ;4U-+pN-$BlhRIRQZ)w z3xleAwm8CvQwAUG#q+(V;j;JcIPUf4B{W-!RgARcB>5T}|5R>4+MUXxJktAzt<$S4 z!{=n|t5&=^6m$t|-B`^@_vf4$Hl*Z0n?wmI`_ZVqnbxDK-Dh8-ofyx5_8xEbqHz%^ z&}*_q#A!bubevGDopM%he%*|6M8ZfD`GS5iVys_+aCM0Rufn?wb8;9nVq@`{3MrS) zsWeGKFGQ_GZp?}JqE_H}c+rv6lIc>VzGbj?JlCq!hDE8BB4&zsB1P2_R;fivfQ|f2 z&d-Qrc5dnfiK%+6FLy!l#to_plWHAG@R#trBVJ>13|G<9h95*-Zn{rxs>EY(!YEZ& zz6qU-S45>OkA9)RSTdTw$Es!lSNUoi_e&<}IiY;-(fzrk8S*|xtBG`uPsSwv_iW?P zxhL9qu0lwR(oZai)3g_OfweUV+^%i>ueD|C%sscnilB1wSH}tuq`|9inIY`Q+A7PF zgyi&{N$eEse_T?NrhfTGc4F8XvRm%k7urL#7#|Pb=K?2DZk5&<0wCH138HO_VMU$* zxc@C0qT@TOBWZehp&x`saQVfEsHoqe^P*$NZS*cqsbSp0u52<3X3CBVwe}d=bJhnq zqV&-@)vwfWXMD?Ek&KyoY!D?(vW{j^VU2nujP zdUm@Rssqpw<=TnAR+6PG5L@_VgI5Ru^+Fok>&!Vr^2b#w6GxD0S3s52QR#;MpKtTD zROXx0pqPGvl&8SZbhYiY#=J0Yex7w?inN#rDa!*=qu&I7;nYEf6JVOYG?H|;+bG-Z z09qsX2fXF^G{fyCDQx9fqV+du{TmNQn+^|PlxH^P86p9+V2MGU!^Os(7f(NBvNSUY z8NZOVZ+r-Nu#J4B)fxzY2s)B^(YoOksP4$kQ$aexhKHjeJAR%4O^(4ZMQhkgeWYWM0chKVR%QIs*QPO;3`PUf7>biaj z_)AI&iwTT;NgJo;CRI!_$x0d4XGo|HBH@(+rb#@hDgA`ZszB}L3m$~gYWzNJ=lRui#b)MyB%%gT5bHJG;Y<2MT-J| zv@&9>ZFy29^v%VYQQLDq+na0E630Zzo*Isy)GMV*pbUuwhj57mKGtdH(gdDCe{U_` z-=U+7Mst!KtYo@3L3h$U3rU3@(xb4xs(YaXK178ds>zP##NJ?vT{PF*<_?s&9OqV+z>-shSdeUs43``vw* zgJhLExeP+N{x&_%{Oa=z;U8=a<77$_AF|rQ*7)q2eJvW6A3RXAvfp`(N3>JGD6}A} z*A`RH0-uV2kxTK3>i&AJ^ts{0`#98_m8~%hBBIc8{j!NFBn`qs(HQ8_@q56wVUUe> z<7T@oQtK!#*+rWBR=|Ug4;~HDp!2&HFA(merTaZIW3A`up%d_^u}qhWfKB;Vo2IMd zMTzq^C6-uKy8CjvB`mPoPgEEIN1r&*t92#du$4X2cPn%To4s&($=qZ#k|0a@#tOjx zCmkyDX5?cfM(V6;NPA1iU3r==hjf5eE@)*{x?k5~!B*W~zPyTK@5NX2@Lt#nPaYaZ z{qEP7Q50TJdO4uU)v{z*OZ`)B`B+{U=1C`46@iYYX#wm;zI;=R`)b=0@m;MFi82h= zHy%go9Oq!Z)8C@$1&g62;eg$tr-vrD=gO@phlmmH*vBO0McSBs1B>&$h^W9YSHT5a z@IqmfL?Lu=R%l%{9%j0I8Y$i@RA3e?5iya=ZWy%_c?6Fbm0@I-JeQ4zqp}<}q&pkq zDk5CuJHc@UsZf!U1UaSLJ>7d+<`>3}dmXAB^72 zA?cbj0`}(AxPdX;<7J!-t7Z^}S8rgrd{r3gT1J9GVkTW9NxO4CsDwnCzO-gn^rBO# zct_}DF3*erQ`83*H`B1Ol0L&&sM_@L=ls!z+Dk!u0ih*p1 zN3ZXH8T`7$kuWQB?E2&Am2gh;=?X~?X3!RAC8c5Cu5NcrcTwX?uH3B2DbssZOgpRO z=T2FfsCm8)(wSBz#9T|uQW=;=%c*oo?IRH$XFfcvr10opZY~o{q$*4 z?a4vxKtTcHDZr`3YCnu7ot|NDD&Zr4`hAb^n>SUQk1dxnr7kX6-BeJ&JLe~Vc>-!4 zh`W59SIGyduI-Xqe^vKX^|P;@f+yRLSDg4{t&*FYm(3g1R>jhaN)mmW$Q z17|D5r!QG1pAwML;iNHQ#QjoQ$8u=G+iCG|t+YRJq>!efMv#q0@O+6PP~~h}W*6*y zxxX8V1LQn2Mi=~`WFS7+2IDh^%O!YJQL=cfbkcT|!*xo&^M+iSRw(D>Vx^b$fxjTm zB#A|w7`Ha2AT<2rvniq-Qm7LTIz(BCS4X^qL1P#A+;_$4ewkku%Dp%6O#3 zGltMrD`Z@}G);1zJPo`A`(N+hoFrMXXI=H>gx|7|K!t+Q28$I{WHb@oYZ>%Y_XSEs^?M43s4q643)J14ie@L_!Rm0?#N*~ET_LP6s%%e4MHo;$!3(G4c92WCfeB$ZwcIAx;f^4X-X~7RL4$1 zt6M-?)Y=qca=pBOmChm<5IkpFwW(tz{C>%Shl=@3DS_BD-^ltPPX9A;-@tgTMTVzR z1V&RLU4U|S-PLUNCp#Zv8x|0xMfXa5rY)fzE7g$Z@JQtt*vffdUkwVP4;U#baYN~2 zTaSNVt&1hJj<;UN<{5Uwo4|sg)-o5%RbLK!NQnz7TMOSQiZ%_5;PQn1Uy8NQ2 zdgP~jmR!(w+i;mKl4I2S8V-Uy!ZGeeBoT^lD%rUMf_1zys>w>CzDcva!4!yuCC~jF zv|5fFjlK-wDi2OC=hR^Fqe(rU-^;UGE0nh3Q|xeP)a4C26z_A?eE9BzQyPW}+9AS;jHE&CW+}Q4OpJ@LWz{ zF`=;RkTO#v%k%pRW@(&|cY;`!AGS2JN(vP~cv*K#v-r!~KY<|UU*RmtMo+pYnGlfm zhqWqF?_fj7@}&AW`l)F1kpHF`JGAmJD(CF?;Y$2LVOA154j$V&j8&N_qplS> z6=Jly_R-X{bd-A@d_XU0Q5Sk$U%X%2b+MdvqL4J;ViK(J1x9Wia}?$?*EW-x$bGuG zkq0YMkh^!gC;wr0YkXs17PBCbjEL>chP3H^`WMZl_VhqbSRD@}LTEI2bz-y7vberT zPO?70(`*?nX17x$l#Mp5-I?0UHXRnYx8r74A8`CZukEZCW|PMeUTx%~Vjm8=P!~30 z7;P_iUmw9ZrP-PI_$xfS#7+tiW1SPac`c7#f2Bt2rl0zvsr^W*PxU|&(jvlYWbe{v z`RoKj@!uvzsg*k<2xiu>Sa86 zHBL_$(14mwJDHG%$>q`gNdq&+bFpSoHafF<^pky!Cgh>g7n_<-rI_z)>VxcT;!A`{ z%l+h7%?uqDa(i1M7^*B&U)%#A5FKi5zhaoV`(>725W?>&Gxd&nI4AqNl9wf;GA=dY(n>jKYzk zZ%-U#ip2cM4xhWUR`}hK^?_Q8xZ55c>}Eku@MWS%gHN}WlJ0paX|`B^uY<#&DyJ<8 zqAL(FPCrml_JJr1ms@cLnaED?bX;kPpNHQZDHKyQ9Bx}-1?mm0G&^0&ZqI@}m0R?D zlj9Ym?6-U}y=6XshAe~&yCugDh(WtR!daxYh=7LUjW+{^46~xu)!>=39Bd^dQ;bNa zsP-5Acf!6YM9id~xB6)h4N}QJ$!ELj}o~H7NC^SB_N$2pPsvdwAEXYuU zZJ@Rd<8KasHsdL6P3qCn z9cXMrFzf`Ivb-4)5A*#zcGxo*JWOSfmskwX{7%M!v_Stm zK_8tXigZ!#ber*7(wcXsmX6)7#=ud>5vo#x&Upr3U>9d2scEf3Fz$=nv_t(K#wpJp z)NA2j^`0Nt#dx<-dlB0Yy?*||pp{B)-QiwCC!S?nK{)r6ga%Qg z&brz+B1g0Cdv3b?tfebjQd38mwJDNiCyKxBzgDGkAIlP5&0 zYf)Xr0MF0%rYRXXNh$e5--2kT6Rn@*0Z3cx=uI22Rr}~Lb~q?+B`5iCW1J?l>OJJ3 zuD$7@Qs*=KSlur>gpMAvf&271 zquXp26RaSJ3#9~uMm@%9P3NUGaN^_=dubeoX;byI`4u05-0{a0ex})MGKbLOlPbN2 zgwDQDtl9CVT8j~yx@M7E#MtRLH;X|*|00sze&|8Q=89cK5F)`4mh%DdUlZ}22_%9U_#hfLVBfnBvc_ZleQ>Lc-w zAdUE=h2+WD0ZRBmhkUJ(S(#%@Vnrbm|2*miP%&M}-)X1E59bu3cnYTNev>QOm=x(`%E1(>4Lpb0r;$*yUJpM?It7ArCvMm zg?zHwX>}xpMG=YaT?aw;zw1~(iWRDrB3mole7o!M0#;VekIf&XMHeP8rA)+s`i+9_ z1=0LUDi8X`0CVQ~{K=d*z06d6@#t~<;}0O?)p}>{;=Ad3^PJFUeb*}tZBgDM-1Wpx z4GEkTdBX{Ym@l!&{Gi5QRplxSuXC@iCvze#quu5>ITJz5a$uY7Y5gL5|8ZQodsqYa z_toh7fCAZ@;gYND=<(um0bS5sO3`IW>c0zJ(^Y>lR7CpFY=?gIP2FnteQ}cu`-b%l zF-n_k*}yRZVn;xghmVoSVgsaSOEYEoP}0PdAaz?Shh~k~@nZ-${-<*do8bqU(!jTG z$Q|Xy1nh!bHEoZvwH+AKvR^dg=%rvCbC%A%AlLD+;fPDmJ1N|tfS!%ju#f-jJoxG& zSI!WL`JATCfVMAhyQ-SZYov}<#488m)PI~l6>fLJmLGqsvz*%y>evva*I>mDb=f06f$3;a^LN!#sPiy?z{|NP>Ogm!t+Rd4v7QjYXCu(t-Mt0Iw$mY+t&8pjJuf}o ze>4}Z1+wKiJq^#Q&UfK2UEqj3a7n0Z5kKycBdI<@=h+P7BHoM@E4*KKJu)ioI(tBr z@U+{eIz~7DH_@6WxUL4O=VxQL*m4fnVf&1DHF~EXGh!gN&fcV9{W)N!oB{0`Bs`y$ zOP+fPm?%G}$wjVM*SO*1D|o86IUV5OKwLZ2)Jcg)K|ie-+rTX|McOiQ6bFF*-!H3NUpL#&9^l@4OC=?!(IU$OuH13B9>A3I~^e@y$lKg8;4A zg}BNe!2-kDCxN8{lpo9gOsViyhy{Nm(zl2prF+?G z6*uY~VpSdzZ#+Vin0FXLr^?Lp-Fa9!uyQy@%!F>Mi0-6^-g_Z4**zJzQC)LA>A(at zLIgv|xS`k$qgUnOub%u}t)A<*vf#1;v%{;6%HR;`Es4=e47io?wf*b+aDoW)b ziD7jGb=yAF`JecM`*DWl{+@^^Q_P65Q@`=u%gZ^7S;>RyEOw_Bd1$Z<+zIT@u4CQp*EtMT`hL{HzwmnLgkdOUycuFlpG- zS@-5>V}Nh#D2#E1OATEu90`LPYoFXZ><7XS@uz?5C-o14M?9@r;R(n|;ja#!;9;IV zSfi{Ydc7tqOB@hl7UNV44rS&vmD+oRObQdia4)$(UHirVh6jH!M-QGF2yw zZROIz#xnbWvX8^;1M25{ic^z-01(I;v{xlWXP=N%*7}gJ_DL(0JB$>hOE_`%O(2 zEi$>uCblhR1g*O%7SQIEl2ZmZS@Q2_zt^iqABWn);J;ZoViPykq8 zg3kx4eC%2(bCn@ter|^H=e?@EB1(+UZql{7uV@IWoCD%k6VRrTWHV%+U>bRn*aCHD zWgGTX3Z*nJ7PFW4RB~uz9)p=~ry$+&##kg>Sbg5t??)073#KBN*9clthoT=6F^0J_~V>WKO%P%jH5%C95`$FrO z$Ln6MJR24WICq~X7<0sTIAqpVdO`7_979>FC(#_!lHDH0rhT8%tQ8cU{Hl_>Pv%B9 zT7|y;M}N%zn1EOQXD++__nGxH9a+=SO#}be$)={J#IE;avBEo=*bC-aX4bT7(!X1N zfbk`ZjL;<~2ZMVTq-7CdG`ujuRq>u^Si@L!RKIbuQ;B~yIxqsT1#Oz_D z9QC-55hYa9it6H+TFhp(Fe*n)v(ut+*o*Gw7MrS-WM*_H7VE9~lkcZz4V_y0MSfZN(Ywy0lccU!>42MTe+)76?(G{_~8 zJ9EX@hfXB7?B&WuVN9k_%0`zhY7<)6)jiimRhnx`G$+~#E|Pas3e`zg_<32NU?&L& zb~i6_&EeSYP@~CP=RzLKnKY36_-fG1#~%hxGi>p8xj2E}F6YbOFezTDm>WgG)a&}8 zDe2h)vo4mXRlvraP^&fw`^Pd^F=(pwK2UJ|Ij;Fyl*B8fzYJM(|wc^rJ1#*p}#>nCai8WB9 z&dD-m{=H3cK#$!HR(daXn%R92+Aji9#vM1Kx(o~ta;qqC>I|vgur_1Vl1QVjkKV>+ z%a5i;-?+3am)to|-g$HvkA2zNczr;_VL9|g9&vX(Y#_N=S;s0A_B97cAEJAfS5{a1 zQ|cY(br)A_NLU^*<^LlJUz+BCn`RXQhZahe3|&n;C#@Tp1uKNItdB#4IH2F8%1(R% zu@SHvmaY2EVdCFU0)p7zIRkP_(w;A~w)pBTAp47~)?%o8v$5)p(0GcnLPqy^9^>j1 zuL5e}3VV-bhK$h-OVLMGT^W|vxw6{cg0O)i1;gSp?sn(tfzf;!EOL<&_=Hf}3!iV# zOQHcF!tmG8TUBI0@8n=KaAC4fKOv^F?)#f=+qSvAoyyAi6mc{WktiM=^&d@^Yt0Y( zD_7a{X#?=XU|*~ZWCqTZtET9f(^J8H9(_X8=w5@`;j)H}t`=KUg$|H9oke2I@b%#wFMm-olqbP`0OtAU##+;) zGxcKc8|}=(dlEUr^XG1b)$-_GFw+P7I?-&j7GhDAAGFgDw*4eK|DwD1>qov!tnQ+G za{IfqnW|O#hbXV!yQ+DqmcP9NYF;HaZm^;Uw(Pp5v`Q|mc)I(+vX$R+HLSv(}$$G14{0&F7VhRQXa!qKFHYS0_RF$HsNo{Z^bs z4<<6d2A-6aDuHn)P1rrMs)inQVEjsF*7`~+;{EzE_dMn1!00+EXrFzvEfay5=@B~_ zzCnnKJO4si#;~PabY35ht$VK+mD|c9YUj83hn2ueqmxa`s-~tf9YNOBHFzBd6eIZ9 zQE0eOAq7}M=%kqMQOVhE+iJ32XMYpBZD9L`d#BZGodb!|{7$b6`Gdp!2lBc}(rr%P z&|8`83!l(ye2m|xpV)_3-M>#k+s&sCM7{#mgdo4$+rsV6{Evd2);~f*!k9(9_l#Sos|BXN8bGQn zqGq#|)a&p4*+<6RO&XsKN1-w@B}65q&ar&^iNDAa?QQ9(hS{2<6P+rK`d7ri&kCJi zJK7jO$fN;GcMb|I+xPuKwH^ZXS_~kw?s8($9E^RlxNRAbql4Zd`qx)?^qu#YUYUrl zt}OuGYtZUj+pX{=(%JQe+f5p`Rh`q~!ij30QW4X->biUTRrWu=Bmw3#SUp(nZ}$bF zOX1EF9kkw+w~vD^>VueB&VcQ$X+P>y^5L(Hqy zi63bQ9Z{y;&6wg}0@!D7R+k^=Jb{G-D2Idrj*w{@w3R~MvPHX_u^RxJ(4+4Jd4Hza zTOU@XgM2ii+m)_fl8q<_K^8Y=u}0r$9LLJ9r%tb5R>B#Z>b6TUF5TBy&hQBsAdZ5S zRtT1JjaT{Bbf3HlLE?+5W6q9gO0qx(V|LLLjrBF?zRy9RM|0Kt?VgiV6E16to~65+-!=jfMm2KmZtCRpiyRmU#We zKwApCD9Ki$H=}2tO$`xiNLIJ|K|;*Flw77o1FxCGim^-FM%hW_IPd9zyF)Me=VKwO z+0QXE3`Gu*wH_Yvbf1rE>F&~(?#U_;f5iDNwy4sMtfs$8MQUg-Ug0>fvLyM!rSvSAO=m9ag|a3JV?sD*4_Y?s02P<;h9< z{rVXmDvvwJn78+`YN##{QVTqn0-G}Z`i^fFhIHY)1A~`M(LBjrj&9fL(OB7rV8jE< zoL28vJ?@~bV&>`v);}^N3LVCG6ef}>c?$iMV9gA1RSCB8hVw$d+5cOBs5uJ(!wMHC zIerWyclNbf@Pt=+PP&pN?%y+eN6+dqS7=^lRl3FjhOxEqxeeQc^aMSOX(z97Hf8iC zob?uKv%0q5B_ML*nG{(J{;)CQoPn@f1;HMcyfeS@B&q5j@`Or2>h_=x{7|c*%-MP; zqj{}SrGK-qP^|)W-~4J;K_=u08%ZF*jEKol{u+_xN~}o0x<^l@$0t3SmRG z9vL>-we9NWe5Mu*9B5V4k3pW#2D#%>z@?Ad2ha%RXU!YEP~s*svO&2 z_&X5NB8Ava`<+!nD3d&lUh)GQ-uNfPsHNtR+0GXrPU#?&OeH!kbRnfwLDAqj%c8+{J| zM<6RRF^dyAZk+rbn$5?-4Qzocm)4aQH6)I1hTnoekCdsq5OqYa(ATX=nW?2m&o;1( z7t&&A{E+5qmxG(zY#63-Bjz-O=OOz9nzQ#!EFYlL{*~@MKZmxnW?K9f8Khe*lm4|5 zN5`>z4p-~f-4o)RO7GZ-hvAFA(nc%Rt;+0CJj}Nsa&+sfzHDp-X0$ln7|1w%A!Y`% zH!_jG`Tp&~WACXn(V?eJj_lyz#(zyi>W`~sFw)cRDU18N;~bvke0}L-@18LsFQZD$aGIEMRAC$Tqi+o zT^*2JyN9x@b3!hy(-Ph@GCslZ+R@NIhz5@}a?ued@W~IItgefr3nvc%4&k@+?YFza zUk37L3pU|Y3n6tZ{Gb;kGl%9I4Ep@ZaX^q23d?rCF&D`e4f0E8(*1b>D2^5g(J{vh zrBr~G48<0u_7BK)jwJ#uG=ABNEo7f z#LaBX@c6cqY3Jc+H~x;CnpJm9Z{>0_Y1KzMB3=PrASS~;aD7}*P)qJzOt*9aXEg^3 zuc!i`WF=u&B$dL!6EJHQJYX}ZvqwbSdGX}wD}KgqD1w*U`qiFNnVqo>YyNq=L3|GH z_)p9F0+@0g>OE15w9CWs0h#M_cwEQY+S>87(eNeo30Lmt3fIQ;=PVk+reY`>7+!&c zHI-oyp|1HE63kV^PBf1j$&KSBUlEv{>jdB=2nqD%OFXg~V+%BgxHiUhfdG z4tbhyVCz?9@p@SxfO+HKsgP6JOq4?Na%O^L-%3u7C1$9Ri0{$w%bt5I=%8kJJJ3~u zyK|XtWAVg`ov|`d7|#uW6c|3hDalilT6RfzN=&gE59UwOsBahu`i#+*pY3(4BO^1$h zeh<`aX$^A}u(PvcnX5wYbV}X&+WfnA&Xl>^Gl{cGPeB(Hv7&4i2;1?BhShSqdo zn#ym1&G$mr*X{s~gYM+${BOhhQ`ns#buq_~lo$>uP<0^7YP^Gz3o&l61L5yHY(Iyy zJ$1}zqXnos08}L@ZlCQmx^Jk>rvNR7-&maecqN~BjT>K`=+3kS2;Y8Q3iKhWX@gtY z*Z^F6dA8qF0nBI2_=o74GmxRdA@$tc?yY-Q_fTG(es`Wm4`?8{yH(Hs4$ukLz97II z7SwPMK-%{KGL)f*9ULUoz(aI&{_jU^7drr?FrU}D128auzr%pL#(5eO_&DZtF(^oY zbSw?%h7>}|=XZwz`|`dw06jd69{hfPgO?3Zeqr?;K8el>kpX`rGu0)9VkX=b?qwgYn(K&l!$71xsG4$9l)U`Q#zN!E{Zc=_7qWT{@AeI7TG_)~!R z-6^MClwy^fHG16JC%2T}?t6=nViGYt2BcGqT*tXHI|(q55U>;=A+ES!0$y06)N27^ zV^6Z1{`$Cn@EmTiwA2*^sdxJObq9t4=okV>IM)-Y_a~po&hup z|41UG6YxWz$g|morj4)lRljkkX)Bc$8wEx8j$;L2qJ|Fv$)aj=0)n2q3nFQHxK)1| zIbN(X9f0KU!5^cr1q5eEdEML<%PiyuNTlPBL`uVsAs5|1?S$R1Mf+3}7{BuFRvye! z+72cS0LUzP2c)SZ78EEb#7{sZLVyjOrU5qewY+@tmdQWdd(nOi^c#LEUbEA@D;DT@ z2jvC#p9u))NdEz~{swS15kLtr{q)xR{KUQ*wl+CH z_<-@uK)7G5woh>LY0yC9{`0tHg3q{)o=6n0piJk*q!QN_(PPp zzR2~mLTOE|JX2$le$t=J>b?fNApRo=1;86P_yLUf18I;kzfNoc|K}nV^*#FaBLuMj z8WL$0fHwnx_69JOM*9gIqLL$KfKCfWhQ(rA z03ag38gsjVJeddp9N+KUPX}Tc1Z}InW9gsID5o-z909hK^z`2}c>zIv`6LPQbK}t` zQu^}Kwn{A8YMt-{UsrY||6X@((03sq`9?v-w$7D6e*OrgH|_1gp-ooobDmAo5%cFA zAO*)XQNNQ+LARov(zhQ*$U9nL7(COZa5?mPu=w-6!GvR&a{x}!Q_DGs><6WB*|FAR zb2ky-txMwWP)D-!23}wG&nkstj?U^eKvKl?9V>hoKvn? z_Ati~43i@DkkUd_N-PnuO@7->pWr$+NICe&^j7icc; z`=c-&Fj=zHA%<63*&3_>S9a!Y`O%89{r-#|5oSUM+=j{mGkrjq$x({`bR?VvC6@)z z9OcyROl8P1#Du7($JvURytu^u5W7gHZpc)aXgW2i`{(Nn{1E{rBFQxLk36UDQuj^% z$jW!29SBd~M1X_D(SyG=#s?@j84am$pz|GRL}?3k&PG6i83^@&Wc%-R#Pk6*Eaajm z6vfR%0hhc7^mIg@6NTRS&S{f7TL&*jMKO>`JS5)bckTt6bmXny!XCa|O?XJmL_dM5 z+IWI$O3XxUdKX0iczI6CG<`i%lX~($wC+BmqCcmCi*1HnGz)=GdDF zN&dcUVZ*o5REE_7jxnvy&*XxLe!ey5&Gcr?aAY;-E`9$PhQ;f0Q#q+9Cp-8+0r zs-rS9n_(WVDwyX|{~-R(_l|u3= zR@738C@84L4eHN$u!=>c$MAPn9@}VKh1!f=)70@)0)_og#D~pVAXey9{yb~5gvT!AAg%xnK86ig=m2KQMjNR+n4KY}^Fmij9W6jh~%Cn!HRxmP!qphx_6H}*UhtG+lQ zQP7%ex1{0eJQTpuYRFJA7-19M=CULi3-v!-53+!VEqbqz$YeL{)toWc1+C7ug7?xO zoy~Ic{UT7={em@!HXUNA1yJCjjyb;i!_e1QzD8@Ts24aY_ft|EVRo_H|ofk zfP;R@64}GZG%xZ=LU!!8w2hjcA2D^uJB`*Zvx1fU0=b5P$1Ufg;X7r2#jJMCo9%r5 z#Wt7W9r3y5i`mwtiWz)A^ZI#PIlj4tgo4Wd2y#kMHoGl;`^&{*t;@Zl;3K~rydZJj zxtt@Kjo?JecnsU`Y}K=a~0$S<(27Ag+2C%F-i5BwzyI`rcS8^8 z9cQ|7StUA=0{Ai+O~q%wzZEl>(w218vS-n-UFyjV+^CD#h3=OHu&T@ML5O{UeC5{IE?sU_ISPVMc&|0`?nz`;TO~@z6#@@Z& zjKT_!V7(U5PG$WnoX`~)(?oB`?SrS8DM2@uzW0&j#i|tGQut9eOUa1=*+;I}zQ1Rd zA6|4}uwDET>Bzb^*zcrF@NlZ(OzWgSUX(}nnDVADk}Tf#n0_;+d*n8voM>3*+r8q9 zkVNd$Uw`}GZ{Eo$0*TQ(Shf*A1^DnJMmQ&ED{2XH@niAI8ICY|a;#@Gf4apUsQVjt zc!3r8PXq^yv3K>DdeL|9vDy}m=*1R_jyJrb|5Cq{Ot0IpjtiKlHbaqIOy^vGF0BAWN(YS zBN*hotT5iDNR8Xy+)WD@aPBkUZq+nKxYPVeGe1SpxD)I;fuij&Pv)j$Sma%##)y&2;ZRN1?mHY4V4ki(A{xaCz*z74GpvMjwvy!3h zqRlY;h~*3~fun#rjzm*47Ow}D#Q&?cuMUfH>)Q7isDwy}gn-g1(jZcj0}MlVNOuY% zEz$_mjf60CNDnOxAl(cC14v6t3?1Jyc+Ppxd413K{od>P@h3Ca-p_vaUiV(>zSml# zY}4cssr9lX%~8U!fdo9F8K5nkY134_3Oyq0?RUd+Y41MeSuZS^a03C>XMPRHy~f0Z3*~2WS_JWSeaGVr za2ExPE>OZqOObMOd&CenkI-)jml`mw$Jdn1qYfF0w1%zX%i460OjqJP zNU0XG^|^hDgqb{{uNa9UP^!X05P-Wg&Mxgf>0H{rWr|)F+LVF#(yx!;P2))BFsYZu|U@XYHCc_i; z)TtA!v>I@Ia#lqT{DY8AoOUIr86ye(R@co}rRUoDfa7+J9VZs=CtT{O{1Xs|B~b(5 zA|fYd^7}o947P0 zsnR-fd(_QQ4|_xf6y3&w`hEB!?jqUTA>5M0Qa@=Y(~ycD(bN+vhoL#6x6x*VacN++ zFf0giOdY})bFkW&KB^~$B?QnJgV$jXjXqAuO%$bE)RGKL76=RWu= zij9_p%<)Y5Ecur2)2qj-^?StQIDt$Iw^MN)~ellwS6AHprRAv-J#+lE@` zXGaDv>dr?cp)a-w%HcaH!rKsB&vh|x7#&Y`dpc+~oSAV5BxGpBrvut0bV1mgeDAaG zo`rfgMaMItqn&ixfI67J`z!c{`s+xO2X{QvSh1l%QNFHdVY|vCn|_vBn7^~-svBog zZaH?fm*k|E-Kgv?Z&DhHXfrha$mXl2ekCf3^m2ZJVcY*u$$NUeb~X;H{*WZ)9Fx35 znC~p}*@{%z2zj@+Ro33)U46;wgrT5fGyEw}H!*jQ)zQeIpvU?>Kz^q9RKrLFgWxMM zv?C&_jqT#_R|HdtZtIm*l8VWF`ReOkK^@fFJuhw|m(Ga&oO3=4Bl#rvc{lczHarmc z#TqWT4NW|28hf78Fnvot>i}oZhL?>+P4)f|qR|yKS+1i5M}u~m;l!_^=iX!5b3x*J zJA2u(A6)k8xN7{P-Sur}<5FF2kog2tGTxq3UN*r`)7qIkmtSj>7RDwFWIZz#K|ivH zJ2r1wP_UFK5nxFjAr_RcJzgJ%!KJ33;ahl!U+RIT@lmghrFQ_v{OrNm(%wl}D^91u zoh0@5NpbLOAK2(K_5%k_pVZEgIJ%m88na!)Ij;6M1RJ+in7p}2)wob2)y~(H>fjDT!dV~%)JP&(xcTj=n@>u0#1Mm|5=U>ptv79snBwTgOSqp(OgUGVz~); zz7hT0J@~`{#$!PRDxk}I4E<1M~nI-b3B^qvF<5iY2>)@j=St#~Fk-poH;`n;I z#shz4yAOBC=AbdYtWhCMG{G&~ZY|1r>~yx|B(L@AMM|Y9+0@pX1oXg6Sx&`wh!!N#*GPLpHWWmb5slF4*9MVhRhlnpLu$e_aRlN9c-0{Elbd{As~J6Bs@z|u zX+F|5QFTeJ2VA}&K=M1$KSOSEp6E61Sr;l_R?H7lTHR4^FwkO^z~c!!|7f~XKy<;7 zM-*&Wq4D8Z_OJCS zm^S6R8I|BEcK>C2p!@GE;;AXD6HvCd6pc@#8t z_ww+x1)TUowi=hAo+tf+gx}vBG6P)&jzon+T9oDkhinG<^5q8lrLAx8xiYdN&(Of5 z@i6-ynJs4$t!d!cD9N(&Pe^KG`l(V*jaJCuD$9oO)30q@)YzsK>S50SPm>mNgix@Y)(hpJrFo8 z`U2#{ShODaNb((;zNql%m^WP?1vwT@^h8I0<p zE|^OfT=ZN0k6hBrOC^RId9yF=R=g+?87;L};u9v?=rMv~>zWtXyhCWuDnHDkJfGq4 z5U+nXuB^gX)q#y#=ssQQqc^;d#0mZp3sOz%Nh`Db9C*A)#D{PoHYOzQ%&FBJ#D$@f zN@d%t_7#c730&?$INlckjEUP@jL&90qJPE{Jhknmm^Mn;tC)$Nn<&I$P-_kJAo}VU zjYnR6!Of?%7)inNeA(@Sf)OFGcrN)b>;vJBsYyMKd6r1#khB#=$37!>k2{?Gvepml=3g3%SY%_bk(CS5<#`dYBL^u^uDRpi z-_A-Nq0J4ewZafy5)2(C zNxc8(0uTPdhp3=KxUGXCC$^>{ZD3eH$*P4~fEX%|R##>q^rwAuSzzLE*ZbX$dPsnp z>%ehxGlY@_k0QHm`F_mQ&cs$@J$6o_=?h8&oIe)c6uTH!1b>|@kn3>e7?dX(O-VA< zv|Hm2X>?fsQB6SrTti7O;G9X8Ep#cl96xT8T7-T|r5V0j(cr+~JY>PK#;Jn={>U<_ zT_Z0kX18u<-e4V=2}*?f;y3oJtq}psuf~$1vZTJ#D-w^v%UwEN!Gc29pcE9fkw9*t zWJeXWsn#i*Z5P81Q%pWo_9p`@;z_Po`*lFoH_5r-@eQdwOQ>aN#II<_IDC~VD{s1s z+o31l`A+Slm`^~wnwTq)txEk)v_!OR_^OPh=9Mq@*WO%9su(gx1`YWIhUOL|)j7r{ zUGt4wZG9yBSOY}RI1hyw&}~o7>GnPiWP{T45U|4SaRc1xR>LjcFM6*BK8&v1vJL=?bI2qDW7@8I^#T=PehfS1}Ingon3(jho` zpkZ2r1xgaB{A^0v%WhU}f1jpm)93l$huu=buU*NtlZM&xJ&?KcRhlz;mPa6B0BS7y zx9{bXa3!uQO;)h_(tTOcHW;&8O9yS(V}RAJqT_M2wDjN8ml|=isiA77+9xjn!BMS2 zlLWlPpII~Ud<~&5atCeN07mk&4`{e~_Cal8l(Qa&bAVSQ!k8O`PSU)Qx01Nkg3+!6bvw(n4L4j2Hr|$mcuo^tDvV#Rb!(_yBf3F^JvE;hnJEiF$GY{=TMp1Wox^X9E9z1)(KHp~Khm4bR2No?B^dLt zppB2U*^4V{nnztv5_4P(8QtV@4LG5`*t~eQ>#AuOp7I%2*?@D<;qxkaVsm^W(QFlAb+Vfq3H&<@g1O{f;V9m#QKF*}pVXa$Dpg?!bFi zKnKXi(LAvhBaLN|hbz*K?vNyF&b-%!5(Z@#2h`zy zJ2(enO$@YOM|`75As>V8XH>gd^&@*)P0cyPQcw)CockJ06AMM5tv;c|+N-(mFEv`g z!=&_ZI}l{snDpMv5V_f^4|I;=QVu|YO*tX;tl=$;7O+ggRk&KY#7%e@CT+-fbW#5R zZdpk4hVX`ibpqZGwQFCk0^jTM?Jf9=8O~=%TN&nXmm;VKW;8|P zqn;d>T<QFCM7DzU|&o0i+%!FQ=*lcu7Og%R|+3;=v%SXIo zEDgw`=@TcH$M0SX{Ln+pfRbkyQ#K7MCx_O|dSqIc_KoYx0LXBqJAEX*Gdy&2<_Ot9 zKg0LxP&+&rIKtphk^9o2+TfPujK!N^t-4GGn`e2^^3k2+9On43ib^$xbpnvo+4FA~ z?u)oykhrgz+3YvLOxxmQgTx0{G|5k{;dpvmhxtr&U#~3v?1i~D+_I;ETG7`t)DC5R zsiJ;)3SvGG{Xl0}4qpukyOEiElTi35>`1XU@Z_Ef43-t5-@u#SL&Bj=L!-uALAyh}uKcYJCC?S&h!TmO ztY|*JAFcL{C0jFE^`0?+Zew=w_ynxoKh47g_G>3M6xaM{N21>&AukfY6|H_*%k z_5_YEoPJFV^;uL&HB^1k`CIgUvbpEs!sooQG6%z$7NbR`C-wLxYpS=p&>oLH;qcof z<9=-tY&FSDuEb5{cW+fKhXq$gc3hz^h)q-D6U;$?TScd64LfK$u9-61Z}3iO-Bl(ZADqrY8uHz?)L*$2JsK6>EL2iTIyl%4NGuuqpP0-2~EYzaR zyV8Y`q}UDd;vPA}+bFx4v&@fA?ny{nMY|fp(6|&xr3E7dlt68^z%$k(}U4b2qxEr}3nRGZJTBYc0lB!joT!j(F8XxF}&RqER zESs^j=b;fs!FLwC$eBtmqmv_s<|nSq)D90n(gKVCsaPpsH^X&Xt`;-rFL7@?eRv)c zZCI5Vb|c`IJ5+T{}*=)Zo{S1Rq-ab8tqYn z!z^AFyw(x{iFcP>hJ+KJ0Pm`J_^8;g!v~$0@zQ7lS%$(d4Kdf$yCm+&uz8=4{vRsm z|Mw8?4?^qDI5M~29)buQbmU>q?0m+bz%1|yag7PBljQPbgC&_4;qOX|-H+~3l|z!+ z<=oR{@d;GS@X-vC0>U0$x}t-jX{~}cA6NFmopKeUA|1WdN_LyoAflD>5fkgbC$UzNxhujQ^BU}rDD}ii}lDr_e4@a zd(vEB#v$=U>(`Ct;WJA6E={%~SRsJ%vJel?@l~-u4v}aXNg4#93+|1y2oG8nz1E!N zb7PE54Pm=;Oro(sDhc@GS9|Yo+q1L0cPL$VrL(s2fZM!6U7jRB+a?(#=1o(BqE#4S zA{Y}h^`xE#C%d*UnLISUkB-1-vX3a-or5+5pYc)^SG*Oh6B z0djj{LUYn`F)zgmL#>akMEw)c)=-H=cCf}QOctb;+Y|mV4km*hS0tK&+kEF2ww7xS zHc^Hj)}na;{2GKB3C-;ox-_ouIi18azC`GA2}GtgS)QNf83^~UFbbP1iE)=jvix-3 zCtckNCpbRDC!_=X%9{YiS6ZKM_6gpT&!FYW4|0%AB)XgN9F19K?7iy`%C6soU@-)z zKlQyNDUXR7gUPIq2c+(DO94Dfv!>ijL)b=xcp+789}SwxeFL5P&$`!t!Jl{pxVxPR zk~N)vjrVVrw0AlaCCK1FqX)6=&rFEVDFNCVHr#2)zI4w`(2>HQ3R4@7%h@b`#?Ifq z{ajb(?KutV7xwqD7OYp5xCa_st`v|Is+O7fdGr)Y|WjNJN<<{?`1U&BEJ4w;DSgK>e)}*zaG@W+Bx&Lpb zFQ*J|P+(xA9AL&dJI3sETpC5NE7~@eL;Aj}_x$WHP;;2nxY~t?Z?}g(0C-*^rnoMd z=<^b*3$Ky|FKm_i+`1S80=wFPcj(M3hF`Du1Pox6zENCcdPwOM6>KAj9S>sPxGImv zrr)3!uStayr05ZRY8Uy(F9qP?LLh|dIRmMwHKsYmt~M|D&#BoRf#D-b_{`!e{vtC=N)}~TGv8FlMWtdy%5|V*S|MNT?ZudZFXDL#8 zo*Lk@^Ju#**%Fhci*s?S?bvUI*hN!L-BJ4J_T1{cG&{0ZYjvNuKsMMl(zK6O2(}n% zS~j3T51!fyIIVb!e0-WvGxMgrfL<{lrNo(xME_ z3LdlnXZBfZF-C>v9hq%S$dFb*Ok>nI%J5Z^1WtA0 zt{f#&eV}c)yU}{s-s-->A^UC}DYZ=&4@5Mb-z+RCm5`#~OMKDF?9OxxL=6;S@{c4e5{P+G`RRwti@ zC^&mr=h!#dm(B)oP_SK^;rhEqOJ3Ld8dn(zAZY@Q>qXdsRd%tDX%!d&st0*1JN{KC zuhao-znFqnj_vW((s;Eo2tHMxTs(W}I>_K8opyXb|Ik0>z7u8?*n~;|x)oPm--aVb zON}3YY5zuM1KCO7rYYv(8JcpyAzUgk{aD>$IjW&XTF#TyY){4?fBJ2Q&~K;wg)2la zK8nMa?p-64v~jg?f2TretVtCE{M?@ff7p>a!SxHMI*QAX&tohUf5m6pF9Sa4_M|Ro zF)oFabr^wg*`=`V`(tDN;`(InAHotsmcLrY^xPcZP&DC;QDp$UOa(=`a$LB5ZsK#b zKP@`JZZL%E)3r(y4sb{?;wKb?5^;pGCx)qBDOa=_`GbfFR z7x^oWVmUM3K8_xte2mA0)}8?-=x5$VMT!5U&iEs%qQFjbXczZGx-~I7F}*v~*>l_x z!{G#MCpw)&WG_8rD?LVy?D|yCWb|0kH&@kcT*%oHLybBVI#q5PlDKuBkuZz7LP}s* zg5m7O^eD<<0s8IB(CY3r)Mr;==XNN@*r#=}n&+2hVS;nuvrFG%64BPq#f_B3e!B?Y zo@11i*4NeStDiW2*wyQ@-$`<|>LJ?y!8wxuzW%Vf?)bzA@n$Ngtekf48^-SDW1+82 zFPUq~=dhJ`SL;^4VX!08QARP#nk_M>E}-gI51j2AEC-Us3jg4(wR3$}!haBRy%q4- zKuNEnvSYG%b|f8)bM~z6Y^&j?L!qISNRCP2sH){B(T66EF{!~h+0|K*LL;wL zck60rDcZma4r}s#_vh3lB&n5C=q%aBgqMJH`SFpa()BGwNhr{YJBFiEp(dfluJcTt zl{-Ts?qlu?V-Jy&=$q&>CX^iJn!&oiXdj)&3dcL-O^nC8QL8gt<|KTz-||Mitm|KQKN>18l@A7@ z9X(FkaE@VVrhJm7w~@)lF;ygy93f3q&?nj-k3r;?ga%>AGM)4h0wW_Gs_4TJhYx08 zYSr8P5Pm4K1y|{Ze+RdWo4>^!Lo)!XXEMBv5@&37lp3=N!^d+=v(nXeJ7MmYY(f8zKx1*1u3}a(Au1Bw2RILm@ z(u+#=L;NVT)mo0@WvVIfYU^YiXHw^ajQV5*v(eFH-%&#EVg9|{41VmI7YX|QN4>+) ztYwvZ!(DNc7RVB<^1j4_x;A?<-gv|G#YxevFNRil{aZ>(9MhHuC0^w@@s{F6Cet1F zLN4#~|HNkXb2LZnllkdUwgIzMt_rBZ z{8!`Ivxvv(Ka6=}ZHs<}8F~}fB=PyKfnmgGmwiIKb|Y3yzoDz8 zMYp*3+r((PYKpGH=)II^%T8|zoEHUMb!ED+lUR=9ED!U`C%VhAFnsBnyQtWEwIa&4$XBu> z+Rp>Fk`CP6UObzbv9F-$Suv=o@#q}BN?J5%TJD5j!UZqNIG*TvZB@00i?Pp_-qs2I zbB_gBzg=5S#mpJNC+o)Yz=;!LeE~Pg^VQDmG9@TS2TVX9wtkICq1{FGtl~{K(PaX{ zObc&A%*M94(Wo9A>W*!4RC$MvkAe6~{t$Dl zcnDzxAy}4Vk{T>yzP$K}xR9LJxr^>}=wp`FgNjD_9-HVIV!K?bl1X;3mv6-kVbJSj zdJ8qnp(?FZmxUi_vj0Q)G=mkX6XWeWQFE~SC4LX2F0yRec@f)BJE@S}!DrQ{*_*JP zyOYhci5sm(C>Euv9g^Yfz4d~S;lkI`T`%rd;D@O%&RhXmDeO%?Y^+*Q(Ol9!N*Id$ za-uHCR6~cpWMjfD9E_ERXsUlmyFIN|@VotB_n~W9JXxc4fWZ4_N0ChLpC2#g747p;CwnQh#@FX6w}MCnQsqt8Ee_)g1X3|anQ9PaWG+eODfoV%B6(*9lM zTe>71n+qq>rh9m<`WyL$7IOqpucuMSEWt2&JY(}F`6DCCle~q?Y|BqCWFD^Dd^uBu_K^%Cq)#ybD#m-G}ajIMHJ)i1;#)J*G>G(Py-7inR zhhV!EWZH&WcyzbGvuW~F|%G?N2m+inKQHHi6()uR!Aue!tZ9zXVSpg$E?=n z^3?Wg2K?qEj8t_DNPgqv0}Ec{C@Dk$)!k}eQbep1DAvSY^wKYn{8QsMEdBGSD1oBL zeC}J+y6DkGfY-J+F>|o_L7pxry4t^WXID1~^yX_Wer+jDDM>itV>wvkL?; zJ|c<_em2xyb&TUWn{z#TNQnX~2qA-A4A00(Fuj*tq$i#)gx099r)=>f&+=iu)4bQ@ zEjvf>W*U~5(2HQq&ZCcbFKHElWzQWhJsOQ3^NsTp}ac z1d4@UOSTrWHlOSvg$l3cef#pje9FBqQD}oxU=Hmn{+xgL zOhENm-vOnbH`m!p$=3UG7#mTmY#?XyCh53&X;~j;w_n8*`{Ta0>=tcVUdFilfpz6Y z$BII5PnPmQRiW4JPp2qATg6E#?#hYeX%_S&yNjl>@)Z5@6v^u9bP3@m`wd+|_wBSi zo+oU+uM`};O%^tI{AI9jytsB@d}JN|5uO}aVC{lB65qFU%`Y=sm;FS#bl!gD&y^lx zAN1-s!j6Bm<9d5hJJaZe#0HSX3DtG{rhcbPi(1acK+sv3o@h^}ved!>zx?q>WD_^s zrYUbHK52|l8!OPJp=8jywCrj4I4fdwh5xl6Nr_(4**7~m^Jr68*d05MCj&YtkLmA_ z{#YI|#P{C-9j5Sd=Ysp~o9tccFRU-V9r((WU)xqH`jYDi}ahKUUk2Pi7 zezIMqh_T)-EOD2otln|l;A~@RFMtHg&9~dc`AE`egue(J#*@2->GYZ!TGUWEUD~Lx zU=@4^(5LZ5(tmsNY@1~BU7Eh<@?cmp#FouT_LPvVt?SboPN4(w=Flyk0`e5zAI@CJ z{n9Qopd-1ik`m!(xuU770h8t;YyKL^$N+&6`EGl4t7n3RX}voQwsS%vql6#;~GfJd!ziWQwAw + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml new file mode 100644 index 000000000..60ab8f0aa --- /dev/null +++ b/event-asynchronous/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + event-asynchronous + + + junit + junit + test + + + diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java new file mode 100644 index 000000000..3375fd4ec --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -0,0 +1,185 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Scanner; + +/** + * + * This application demonstrates the Event-based Asynchronous pattern. Essentially, users (of the pattern) may + * choose to run events in an Asynchronous or Synchronous mode. There can be multiple Asynchronous events running at + * once but only one Synchronous event can run at a time. Asynchronous events are synonymous to multi-threads. The key + * point here is that the threads run in the background and the user is free to carry on with other processes. Once an + * event is complete, the appropriate listener/callback method will be called. The listener then proceeds to carry out + * further processing depending on the needs of the user. + * + * The {@link EventManager} manages the events/threads that the user creates. Currently, the supported event operations + * are: start, stop, getStatus. For Synchronous events, the user is unable to + * start another (Synchronous) event if one is already running at the time. The running event would have to either be + * stopped or completed before a new event can be started. + * + * The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many + * of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- + * (1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without + * interrupting your application. (2) Execute multiple operations simultaneously, receiving notifications when each + * completes. (3) Wait for resources to become available without stopping ("hanging") your application. (4) Communicate + * with pending asynchronous operations using the familiar events-and-delegates model. + * + * @see EventManager + * @see Event + * + */ +public class App { + + boolean interactiveMode = false; + + public static void main(String[] args) { + App app = new App(); + + app.setUp(); + app.run(); + } + + /** + * App can run in interactive mode or not. Interactive mode == Allow user interaction with command line. + * Non-interactive is a quick sequential run through the available {@link EventManager} operations. + */ + public void setUp() { + Properties prop = new Properties(); + String propFileName = "config.properties"; + + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + + if (inputStream != null) { + try { + prop.load(inputStream); + } catch (IOException e) { + } + String property = prop.getProperty("INTERACTIVE_MODE"); + if (property.equalsIgnoreCase("YES")) { + interactiveMode = true; + } + } + } + + public void run() { + if (interactiveMode) { + runInteractiveMode(); + } else { + quickRun(); + } + } + + public void quickRun() { + EventManager eventManager = new EventManager(); + + try { + // Create an Asynchronous event. + int aEventID = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventID + "] has been created."); + eventManager.startEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been started."); + + // Create a Synchronous event. + int sEventID = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventID + "] has been created."); + eventManager.startEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been started."); + + eventManager.getStatus(aEventID); + eventManager.getStatus(sEventID); + + eventManager.stopEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been stopped."); + eventManager.stopEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been stopped."); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + public void runInteractiveMode() { + EventManager eventManager = new EventManager(); + + Scanner s = new Scanner(System.in); + int option = 0; + option = -1; + while (option != 5) { + System.out + .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + System.out.print("Choose [1,2,3,4,5]: "); + option = s.nextInt(); + + if (option == 1) { + s.nextLine(); + System.out.print("(A)sync or (S)ync event?: "); + String eventType = s.nextLine(); + System.out.print("How long should this event run for (in seconds)?: "); + int eventTime = s.nextInt(); + if (eventType.equalsIgnoreCase("A")) { + try { + int eventID = eventManager.createAsyncEvent(eventTime); + System.out.println("Event [" + eventID + "] has been created."); + eventManager.startEvent(eventID); + System.out.println("Event [" + eventID + "] has been started."); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (eventType.equalsIgnoreCase("S")) { + try { + int eventID = eventManager.createSyncEvent(eventTime); + System.out.println("Event [" + eventID + "] has been created."); + eventManager.startEvent(eventID); + System.out.println("Event [" + eventID + "] has been started."); + } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException + | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else { + System.out.println("Unknown event type."); + } + } else if (option == 2) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.stopEvent(eventID); + System.out.println("Event [" + eventID + "] has been stopped."); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 3) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.getStatus(eventID); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 4) { + eventManager.getStatusOfAllEvents(); + } + } + + s.close(); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java new file mode 100644 index 000000000..48dc37236 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -0,0 +1,88 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +/** + * + * Each Event runs as a separate/individual thread. + * + */ +public class Event implements IEvent, Runnable { + + private int eventID; + private int eventTime; + private Thread thread; + private long counter = 0; + private boolean isComplete = false; + private ThreadCompleteListener eventListener; + + public Event(int eventID, int eventTime) { + this.eventID = eventID; + this.eventTime = eventTime; + } + + @Override + public void start() { + thread = new Thread(this); + thread.start(); + } + + @Override + public void stop() { + thread.interrupt(); + } + + @Override + public void status() { + if (!isComplete) { + System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + } else { + System.out.println("[" + eventID + "] I am done."); + } + } + + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime * 1000); + while (System.currentTimeMillis() < endTime) { + try { + counter += 1; + Thread.sleep(5000); // Sleep for 5 seconds. + } catch (InterruptedException e) { + return; + } + } + isComplete = true; + notifyListener(); + } + + public final void addListener(final ThreadCompleteListener listener) { + this.eventListener = listener; + } + + public final void removeListener(final ThreadCompleteListener listener) { + this.eventListener = null; + } + + private final void notifyListener() { + if (eventListener != null) { + eventListener.notifyOfThreadComplete(eventID); + } + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java new file mode 100644 index 000000000..77c1d479b --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class EventDoesNotExistException extends Exception { + + private static final long serialVersionUID = -3398463738273811509L; + + public EventDoesNotExistException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java new file mode 100644 index 000000000..305548111 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -0,0 +1,148 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + * EventManager handles and maintains a pool of event threads. {@link Event} threads are created upon user request. Thre + * are two types of events; Asynchronous and Synchronous. There can be multiple Asynchronous events running at once but + * only one Synchronous event running at a time. Currently supported event operations are: start, stop, and getStatus. + * Once an event is complete, it then notifies EventManager through a listener. The EventManager then takes the event + * out of the pool. + * + */ +public class EventManager implements ThreadCompleteListener { + + private int minID = 1; + private int maxID = Integer.MAX_VALUE - 1; // Be cautious of overflows. + private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) + private int maxEventTime = 1800; // in seconds / 30 minutes. + private int currentlyRunningSyncEvent = -1; + private Random rand; + private Map eventPool; + + public EventManager() { + rand = new Random(1); + eventPool = new ConcurrentHashMap(maxRunningEvents); + + } + + // Create a Synchronous event. + public int createSyncEvent(int eventTime) + throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { + int eventID = createEvent(eventTime); + if (currentlyRunningSyncEvent != -1) { + throw new InvalidOperationException( + "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); + } + currentlyRunningSyncEvent = eventID; + + return eventID; + } + + // Create an Asynchronous event. + public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + return createEvent(eventTime); + } + + private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + if (eventPool.size() == maxRunningEvents) { + throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); + } + + if (eventTime >= maxEventTime) { + throw new LongRunningEventException( + "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + } + + int newEventID = generateID(); + + Event newEvent = new Event(newEventID, eventTime); + newEvent.addListener(this); + eventPool.put(newEventID, newEvent); + + return newEventID; + } + + public void startEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).start(); + } + + public void stopEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + if (eventID == currentlyRunningSyncEvent) { + currentlyRunningSyncEvent = -1; + } + + eventPool.get(eventID).stop(); + eventPool.remove(eventID); + } + + public void getStatus(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).status(); + } + + @SuppressWarnings("rawtypes") + public void getStatusOfAllEvents() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).status(); + } + } + + /** + * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most + * Integer.MAX_VALUE - 1. + */ + private int generateID() { + // nextInt is normally exclusive of the top value, + // so add 1 to make it inclusive + int randomNum = rand.nextInt((maxID - minID) + 1) + minID; + while (eventPool.containsKey(randomNum)) { + randomNum = rand.nextInt((maxID - minID) + 1) + minID; + } + + return randomNum; + } + + /** + * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. + */ + @Override + public void notifyOfThreadComplete(int eventID) { + eventPool.get(eventID).status(); + eventPool.remove(eventID); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java new file mode 100644 index 000000000..448c02e84 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -0,0 +1,27 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public interface IEvent { + + public void start(); + + public void stop(); + + public void status(); + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java new file mode 100644 index 000000000..4fd5b0eed --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java @@ -0,0 +1,27 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class InvalidOperationException extends Exception { + + private static final long serialVersionUID = -6191545255213410803L; + + public InvalidOperationException(String message) { + super(message); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java new file mode 100644 index 000000000..6817b1dd8 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class LongRunningEventException extends Exception { + + private static final long serialVersionUID = -483423544320148809L; + + public LongRunningEventException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java new file mode 100644 index 000000000..9f8f2891c --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class MaxNumOfEventsAllowedException extends Exception { + + private static final long serialVersionUID = -8430876973516292695L; + + public MaxNumOfEventsAllowedException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java new file mode 100644 index 000000000..e5c910289 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -0,0 +1,21 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public interface ThreadCompleteListener { + void notifyOfThreadComplete(final int eventID); +} diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties new file mode 100644 index 000000000..edbe90e05 --- /dev/null +++ b/event-asynchronous/src/main/java/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java new file mode 100644 index 000000000..8736fcf77 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java @@ -0,0 +1,32 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import java.io.IOException; + +import org.junit.Test; + +/** + * Tests that EventAsynchronous example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java new file mode 100644 index 000000000..0ab901106 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import org.junit.Before; +import org.junit.Test; + +/** + * + * Application test + * + */ +public class EventAsynchronousTest { + App app; + + @Before + public void setUp() { + app = new App(); + } + + @Test + public void testAsynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int aEventID = eventManager.createAsyncEvent(60); + eventManager.startEvent(aEventID); + eventManager.stopEvent(aEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + eventManager.stopEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testUnsuccessfulSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } +} From e1836fee2f3fe7944faf89ff1b66b137294492ed Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:44:39 +0100 Subject: [PATCH 02/12] Updated parent POM to include new pattern (Event-asynchronous) --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 302346993..058dee5a1 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,7 @@ aggregator-microservices promise page-object + event-asynchronous From f11597136f8bebdcb208a141f8e0fd47ee4dbd1e Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 00:32:05 +0100 Subject: [PATCH 03/12] Fixed Checkstyle errors. --- .../com/iluwatar/event/asynchronous/App.java | 69 +++++++----- .../iluwatar/event/asynchronous/Event.java | 12 +-- .../event/asynchronous/EventManager.java | 102 ++++++++++++------ .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 20 ++-- 5 files changed, 130 insertions(+), 75 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 3375fd4ec..fa6116b46 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -50,6 +50,11 @@ public class App { boolean interactiveMode = false; + /** + * Program entry point. + * + * @param args command line args + */ public static void main(String[] args) { App app = new App(); @@ -71,6 +76,7 @@ public class App { try { prop.load(inputStream); } catch (IOException e) { + System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -79,6 +85,9 @@ public class App { } } + /** + * Run program in either interactive mode or not. + */ public void run() { if (interactiveMode) { runInteractiveMode(); @@ -87,29 +96,32 @@ public class App { } } + /** + * Run program in non-interactive mode. + */ public void quickRun() { EventManager eventManager = new EventManager(); try { // Create an Asynchronous event. - int aEventID = eventManager.createAsyncEvent(60); - System.out.println("Event [" + aEventID + "] has been created."); - eventManager.startEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been started."); + int aEventId = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventId + "] has been created."); + eventManager.startEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventID = eventManager.createSyncEvent(60); - System.out.println("Event [" + sEventID + "] has been created."); - eventManager.startEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been started."); + int sEventId = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventId + "] has been created."); + eventManager.startEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventID); - eventManager.getStatus(sEventID); + eventManager.getStatus(aEventId); + eventManager.getStatus(sEventId); - eventManager.stopEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been stopped."); - eventManager.stopEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been stopped."); + eventManager.stopEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been stopped."); + eventManager.stopEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { @@ -117,6 +129,9 @@ public class App { } } + /** + * Run program in interactive mode. + */ public void runInteractiveMode() { EventManager eventManager = new EventManager(); @@ -137,19 +152,19 @@ public class App { int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventID = eventManager.createAsyncEvent(eventTime); - System.out.println("Event [" + eventID + "] has been created."); - eventManager.startEvent(eventID); - System.out.println("Event [" + eventID + "] has been started."); + int eventId = eventManager.createAsyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventID = eventManager.createSyncEvent(eventTime); - System.out.println("Event [" + eventID + "] has been created."); - eventManager.startEvent(eventID); - System.out.println("Event [" + eventID + "] has been started."); + int eventId = eventManager.createSyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -159,18 +174,18 @@ public class App { } } else if (option == 2) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.stopEvent(eventID); - System.out.println("Event [" + eventID + "] has been stopped."); + eventManager.stopEvent(eventId); + System.out.println("Event [" + eventId + "] has been stopped."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.getStatus(eventID); + eventManager.getStatus(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 48dc37236..4b4fe1d94 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -23,15 +23,15 @@ package com.iluwatar.event.asynchronous; */ public class Event implements IEvent, Runnable { - private int eventID; + private int eventId; private int eventTime; private Thread thread; private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventID, int eventTime) { - this.eventID = eventID; + public Event(int eventId, int eventTime) { + this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +49,9 @@ public class Event implements IEvent, Runnable { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); } else { - System.out.println("[" + eventID + "] I am done."); + System.out.println("[" + eventId + "] I am done."); } } @@ -81,7 +81,7 @@ public class Event implements IEvent, Runnable { private final void notifyListener() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventID); + eventListener.notifyOfThreadComplete(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index 305548111..d3278594f 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,34 +32,53 @@ import java.util.concurrent.ConcurrentHashMap; */ public class EventManager implements ThreadCompleteListener { - private int minID = 1; - private int maxID = Integer.MAX_VALUE - 1; // Be cautious of overflows. + private int minId = 1; + private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) private int maxEventTime = 1800; // in seconds / 30 minutes. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; + /** + * EventManager constructor. + * + */ public EventManager() { rand = new Random(1); eventPool = new ConcurrentHashMap(maxRunningEvents); } - // Create a Synchronous event. + /** + * Create a Synchronous event. + * + * @param eventTime Time an event should run for. + * @return eventId + * @throws MaxNumOfEventsAllowedException When too many events are running at a time. + * @throws InvalidOperationException No new synchronous events can be created when one is already running. + * @throws LongRunningEventException Long running events are not allowed in the app. + */ public int createSyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventID = createEvent(eventTime); + int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } - currentlyRunningSyncEvent = eventID; + currentlyRunningSyncEvent = eventId; - return eventID; + return eventId; } - // Create an Asynchronous event. + /** + * Create an Asynchronous event. + * + * @param eventTime Time an event should run for. + * @return eventId + * @throws MaxNumOfEventsAllowedException When too many events are running at a time. + * @throws LongRunningEventException Long running events are not allowed in the app. + */ public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } @@ -74,44 +93,65 @@ public class EventManager implements ThreadCompleteListener { "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); } - int newEventID = generateID(); + int newEventId = generateId(); - Event newEvent = new Event(newEventID, eventTime); + Event newEvent = new Event(newEventId, eventTime); newEvent.addListener(this); - eventPool.put(newEventID, newEvent); + eventPool.put(newEventId, newEvent); - return newEventID; + return newEventId; } - public void startEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Starts event. + * + * @param eventId The event that needs to be started. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void startEvent(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).start(); + eventPool.get(eventId).start(); } - public void stopEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Stops event. + * + * @param eventId The event that needs to be stopped. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void stopEvent(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - if (eventID == currentlyRunningSyncEvent) { + if (eventId == currentlyRunningSyncEvent) { currentlyRunningSyncEvent = -1; } - eventPool.get(eventID).stop(); - eventPool.remove(eventID); + eventPool.get(eventId).stop(); + eventPool.remove(eventId); } - public void getStatus(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Get status of a running event. + * + * @param eventId The event to inquire status of. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void getStatus(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).status(); + eventPool.get(eventId).status(); } + /** + * Gets status of all running events. + */ @SuppressWarnings("rawtypes") public void getStatusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); @@ -125,12 +165,12 @@ public class EventManager implements ThreadCompleteListener { * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most * Integer.MAX_VALUE - 1. */ - private int generateID() { + private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxID - minID) + 1) + minID; + int randomNum = rand.nextInt((maxId - minId) + 1) + minId; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxID - minID) + 1) + minID; + randomNum = rand.nextInt((maxId - minId) + 1) + minId; } return randomNum; @@ -140,9 +180,9 @@ public class EventManager implements ThreadCompleteListener { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventID) { - eventPool.get(eventID).status(); - eventPool.remove(eventID); + public void notifyOfThreadComplete(int eventId) { + eventPool.get(eventId).status(); + eventPool.remove(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index e5c910289..88f300634 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventID); + void notifyOfThreadComplete(final int eventId); } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 0ab901106..392c7fba6 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -36,9 +36,9 @@ public class EventAsynchronousTest { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventID = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventID); - eventManager.stopEvent(aEventID); + int aEventId = eventManager.createAsyncEvent(60); + eventManager.startEvent(aEventId); + eventManager.stopEvent(aEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,9 +48,9 @@ public class EventAsynchronousTest { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - eventManager.stopEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + eventManager.stopEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); @@ -61,10 +61,10 @@ public class EventAsynchronousTest { public void testUnsuccessfulSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); From 233f1e69f9401611967c8be6cc70f16d2ac18e1b Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 22:00:19 +0100 Subject: [PATCH 04/12] Removed PUBLIC modifiers from IEvent --- .../main/java/com/iluwatar/event/asynchronous/IEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java index 448c02e84..bcd78b6c0 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -18,10 +18,10 @@ package com.iluwatar.event.asynchronous; public interface IEvent { - public void start(); + void start(); - public void stop(); + void stop(); - public void status(); + void status(); } From 9a90f2de1f50557e584d3a0ced4d134c8c702090 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Sun, 11 Sep 2016 18:45:51 +0100 Subject: [PATCH 05/12] Changes based on code review --- event-asynchronous/README.md | 2 + .../etc/event-asynchronous.ucls | 30 +++++---- .../com/iluwatar/event/asynchronous/App.java | 63 ++++++++++--------- .../iluwatar/event/asynchronous/Event.java | 14 ++--- .../event/asynchronous/EventManager.java | 59 ++++++++++++----- .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 37 ++++++----- 7 files changed, 125 insertions(+), 82 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index 59e6e8b33..dde434aba 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -5,6 +5,8 @@ folder: event-asynchronous permalink: /patterns/event-asynchronous/ categories: Other tags: + - difficulty-intermediate + - performance - Java --- diff --git a/event-asynchronous/etc/event-asynchronous.ucls b/event-asynchronous/etc/event-asynchronous.ucls index cc7241044..df09fc28d 100644 --- a/event-asynchronous/etc/event-asynchronous.ucls +++ b/event-asynchronous/etc/event-asynchronous.ucls @@ -65,12 +65,15 @@ - - - - + + + + + + + - + @@ -78,19 +81,24 @@ - + + + - - - - + + + + + + + - + diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index fa6116b46..f951af07c 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -48,6 +48,8 @@ import java.util.Scanner; */ public class App { + public static final String PROP_FILE_NAME = "config.properties"; + boolean interactiveMode = false; /** @@ -68,15 +70,14 @@ public class App { */ public void setUp() { Properties prop = new Properties(); - String propFileName = "config.properties"; - InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME); if (inputStream != null) { try { prop.load(inputStream); } catch (IOException e) { - System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); + System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -104,23 +105,23 @@ public class App { try { // Create an Asynchronous event. - int aEventId = eventManager.createAsyncEvent(60); + int aEventId = eventManager.createAsync(60); System.out.println("Event [" + aEventId + "] has been created."); - eventManager.startEvent(aEventId); + eventManager.start(aEventId); System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventId = eventManager.createSyncEvent(60); + int sEventId = eventManager.create(60); System.out.println("Event [" + sEventId + "] has been created."); - eventManager.startEvent(sEventId); + eventManager.start(sEventId); System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventId); - eventManager.getStatus(sEventId); + eventManager.status(aEventId); + eventManager.status(sEventId); - eventManager.stopEvent(aEventId); + eventManager.cancel(aEventId); System.out.println("Event [" + aEventId + "] has been stopped."); - eventManager.stopEvent(sEventId); + eventManager.cancel(sEventId); System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException @@ -136,35 +137,33 @@ public class App { EventManager eventManager = new EventManager(); Scanner s = new Scanner(System.in); - int option = 0; - option = -1; + int option = -1; while (option != 5) { - System.out - .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + System.out.println("Hello. Would you like to boil some eggs?"); + System.out.println( + "(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); System.out.print("Choose [1,2,3,4,5]: "); option = s.nextInt(); if (option == 1) { s.nextLine(); - System.out.print("(A)sync or (S)ync event?: "); + System.out.print("Boil multiple eggs at once (A) or boil them one-by-one (S)?: "); String eventType = s.nextLine(); - System.out.print("How long should this event run for (in seconds)?: "); + System.out.print("How long should this egg be boiled for (in seconds)?: "); int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventId = eventManager.createAsyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + int eventId = eventManager.createAsync(eventTime); + eventManager.start(eventId); + System.out.println("Egg [" + eventId + "] is being boiled."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventId = eventManager.createSyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + int eventId = eventManager.create(eventTime); + eventManager.start(eventId); + System.out.println("Egg [" + eventId + "] is being boiled."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -173,24 +172,26 @@ public class App { System.out.println("Unknown event type."); } } else if (option == 2) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.stopEvent(eventId); - System.out.println("Event [" + eventId + "] has been stopped."); + eventManager.cancel(eventId); + System.out.println("Egg [" + eventId + "] is removed from boiler."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.getStatus(eventId); + eventManager.status(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 4) { - eventManager.getStatusOfAllEvents(); + eventManager.statusOfAllEvents(); + } else if (option == 5) { + eventManager.shutdown(); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 4b4fe1d94..1cb04acdc 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -26,11 +26,10 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; private Thread thread; - private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventId, int eventTime) { + public Event(final int eventId, final int eventTime) { this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +48,9 @@ public class Event implements IEvent, Runnable { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] is not done."); } else { - System.out.println("[" + eventId + "] I am done."); + System.out.println("[" + eventId + "] is done."); } } @@ -61,14 +60,13 @@ public class Event implements IEvent, Runnable { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - counter += 1; Thread.sleep(5000); // Sleep for 5 seconds. } catch (InterruptedException e) { return; } } isComplete = true; - notifyListener(); + completed(); } public final void addListener(final ThreadCompleteListener listener) { @@ -79,9 +77,9 @@ public class Event implements IEvent, Runnable { this.eventListener = null; } - private final void notifyListener() { + private final void completed() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventId); + eventListener.completedEventHandler(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index d3278594f..e65816cec 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,10 +32,10 @@ import java.util.concurrent.ConcurrentHashMap; */ public class EventManager implements ThreadCompleteListener { - private int minId = 1; - private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. - private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) - private int maxEventTime = 1800; // in seconds / 30 minutes. + public static final int MAX_RUNNING_EVENTS = 1000; // Just don't wanna have too many running events. :) + public static final int MIN_ID = 1; + public static final int MAX_ID = MAX_RUNNING_EVENTS; + public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; @@ -46,7 +46,7 @@ public class EventManager implements ThreadCompleteListener { */ public EventManager() { rand = new Random(1); - eventPool = new ConcurrentHashMap(maxRunningEvents); + eventPool = new ConcurrentHashMap(MAX_RUNNING_EVENTS); } @@ -59,7 +59,7 @@ public class EventManager implements ThreadCompleteListener { * @throws InvalidOperationException No new synchronous events can be created when one is already running. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createSyncEvent(int eventTime) + public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { @@ -79,18 +79,18 @@ public class EventManager implements ThreadCompleteListener { * @throws MaxNumOfEventsAllowedException When too many events are running at a time. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - if (eventPool.size() == maxRunningEvents) { + if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } - if (eventTime >= maxEventTime) { + if (eventTime >= MAX_EVENT_TIME) { throw new LongRunningEventException( - "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again."); } int newEventId = generateId(); @@ -108,7 +108,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event that needs to be started. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void startEvent(int eventId) throws EventDoesNotExistException { + public void start(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -122,7 +122,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event that needs to be stopped. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void stopEvent(int eventId) throws EventDoesNotExistException { + public void cancel(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -141,7 +141,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event to inquire status of. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void getStatus(int eventId) throws EventDoesNotExistException { + public void status(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -153,7 +153,7 @@ public class EventManager implements ThreadCompleteListener { * Gets status of all running events. */ @SuppressWarnings("rawtypes") - public void getStatusOfAllEvents() { + public void statusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); @@ -161,6 +161,18 @@ public class EventManager implements ThreadCompleteListener { } } + /** + * Stop all running events. + */ + @SuppressWarnings("rawtypes") + public void shutdown() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).stop(); + } + } + /** * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most * Integer.MAX_VALUE - 1. @@ -168,9 +180,9 @@ public class EventManager implements ThreadCompleteListener { private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxId - minId) + 1) + minId; + int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxId - minId) + 1) + minId; + randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; } return randomNum; @@ -180,9 +192,22 @@ public class EventManager implements ThreadCompleteListener { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventId) { + public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); eventPool.remove(eventId); } + /** + * Getter method for event pool. + */ + public Map getEventPool() { + return eventPool; + } + + /** + * Get number of currently running Synchronous events. + */ + public int numOfCurrentlyRunningSyncEvent() { + return currentlyRunningSyncEvent; + } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index 88f300634..fd62a3e80 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventId); + void completedEventHandler(final int eventId); } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 392c7fba6..6565d5bad 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -16,6 +16,8 @@ */ package com.iluwatar.event.asynchronous; +import static org.junit.Assert.assertTrue; + import org.junit.Before; import org.junit.Test; @@ -36,9 +38,13 @@ public class EventAsynchronousTest { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventId = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventId); - eventManager.stopEvent(aEventId); + int aEventId = eventManager.createAsync(60); + eventManager.start(aEventId); + assertTrue(eventManager.getEventPool().size() == 1); + assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); + assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() == -1); + eventManager.cancel(aEventId); + assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,25 +54,28 @@ public class EventAsynchronousTest { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - eventManager.stopEvent(sEventId); + int sEventId = eventManager.create(60); + eventManager.start(sEventId); + assertTrue(eventManager.getEventPool().size() == 1); + assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); + assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() != -1); + eventManager.cancel(sEventId); + assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); } } - @Test - public void testUnsuccessfulSynchronousEvent() { + @Test(expected = InvalidOperationException.class) + public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException - | InvalidOperationException e) { + int sEventId = eventManager.create(60); + eventManager.start(sEventId); + sEventId = eventManager.create(60); + eventManager.start(sEventId); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } From dbd605e3786eb0d90ded55436976784865328d53 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:04 +0100 Subject: [PATCH 06/12] Changes based on latest code review --- .../com/iluwatar/event/asynchronous/App.java | 29 +++++++++++-------- .../iluwatar/event/asynchronous/Event.java | 17 ++++++++++- .../event/asynchronous/EventManager.java | 13 ++++++--- .../src/main/java/config.properties | 2 +- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index f951af07c..65ae02e56 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -138,11 +138,10 @@ public class App { Scanner s = new Scanner(System.in); int option = -1; - while (option != 5) { + while (option != 4) { System.out.println("Hello. Would you like to boil some eggs?"); - System.out.println( - "(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); - System.out.print("Choose [1,2,3,4,5]: "); + System.out.println("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT"); + System.out.print("Choose [1,2,3,4]: "); option = s.nextInt(); if (option == 1) { @@ -181,16 +180,22 @@ public class App { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Which egg?: "); - int eventId = s.nextInt(); - try { - eventManager.status(eventId); - } catch (EventDoesNotExistException e) { - System.out.println(e.getMessage()); + s.nextLine(); + System.out.print("Just one egg (O) OR all of them (A) ?: "); + String eggChoice = s.nextLine(); + + if (eggChoice.equalsIgnoreCase("O")) { + System.out.print("Which egg?: "); + int eventId = s.nextInt(); + try { + eventManager.status(eventId); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (eggChoice.equalsIgnoreCase("A")) { + eventManager.statusOfAllEvents(); } } else if (option == 4) { - eventManager.statusOfAllEvents(); - } else if (option == 5) { eventManager.shutdown(); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 1cb04acdc..c2e14ad68 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -25,13 +25,25 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; + private boolean isSynchronous; private Thread thread; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(final int eventId, final int eventTime) { + /** + * + * @param eventId event ID + * @param eventTime event time + * @param isSynchronous is of synchronous type + */ + public Event(final int eventId, final int eventTime, final boolean isSynchronous) { this.eventId = eventId; this.eventTime = eventTime; + this.isSynchronous = isSynchronous; + } + + public boolean isSynchronous() { + return isSynchronous; } @Override @@ -42,6 +54,9 @@ public class Event implements IEvent, Runnable { @Override public void stop() { + if (null == thread) { + return; + } thread.interrupt(); } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index e65816cec..dae995e38 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -61,11 +61,12 @@ public class EventManager implements ThreadCompleteListener { */ public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } + + int eventId = createEvent(eventTime, true); currentlyRunningSyncEvent = eventId; return eventId; @@ -80,10 +81,11 @@ public class EventManager implements ThreadCompleteListener { * @throws LongRunningEventException Long running events are not allowed in the app. */ public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - return createEvent(eventTime); + return createEvent(eventTime, false); } - private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + private int createEvent(int eventTime, boolean isSynchronous) + throws MaxNumOfEventsAllowedException, LongRunningEventException { if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } @@ -95,7 +97,7 @@ public class EventManager implements ThreadCompleteListener { int newEventId = generateId(); - Event newEvent = new Event(newEventId, eventTime); + Event newEvent = new Event(newEventId, eventTime, isSynchronous); newEvent.addListener(this); eventPool.put(newEventId, newEvent); @@ -194,6 +196,9 @@ public class EventManager implements ThreadCompleteListener { @Override public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); + if (eventPool.get(eventId).isSynchronous()) { + currentlyRunningSyncEvent = -1; + } eventPool.remove(eventId); } diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index edbe90e05..7216f665d 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=NO \ No newline at end of file +INTERACTIVE_MODE=YES \ No newline at end of file From 371b262a5135484d83efb0666269af0a55c6f185 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:23 +0100 Subject: [PATCH 07/12] Changes based on latest code review --- event-asynchronous/src/main/java/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index 7216f665d..edbe90e05 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 12544caa759fa1ac0fec7bd5bde5dae9d7a4a082 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:05:11 +0100 Subject: [PATCH 08/12] Changes based on review feedback. --- event-asynchronous/README.md | 2 +- .../com/iluwatar/event/asynchronous/App.java | 12 ++--- .../iluwatar/event/asynchronous/Event.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 52 +++++++++++++++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index dde434aba..ef35d0b38 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -3,7 +3,7 @@ layout: pattern title: Event-based Asynchronous folder: event-asynchronous permalink: /patterns/event-asynchronous/ -categories: Other +categories: Concurrency tags: - difficulty-intermediate - performance diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 65ae02e56..5a2565940 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -106,23 +106,23 @@ public class App { try { // Create an Asynchronous event. int aEventId = eventManager.createAsync(60); - System.out.println("Event [" + aEventId + "] has been created."); + System.out.println("Async Event [" + aEventId + "] has been created."); eventManager.start(aEventId); - System.out.println("Event [" + aEventId + "] has been started."); + System.out.println("Async Event [" + aEventId + "] has been started."); // Create a Synchronous event. int sEventId = eventManager.create(60); - System.out.println("Event [" + sEventId + "] has been created."); + System.out.println("Sync Event [" + sEventId + "] has been created."); eventManager.start(sEventId); - System.out.println("Event [" + sEventId + "] has been started."); + System.out.println("Sync Event [" + sEventId + "] has been started."); eventManager.status(aEventId); eventManager.status(sEventId); eventManager.cancel(aEventId); - System.out.println("Event [" + aEventId + "] has been stopped."); + System.out.println("Async Event [" + aEventId + "] has been stopped."); eventManager.cancel(sEventId); - System.out.println("Event [" + sEventId + "] has been stopped."); + System.out.println("Sync Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index c2e14ad68..5e557fff2 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -75,7 +75,7 @@ public class Event implements IEvent, Runnable { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - Thread.sleep(5000); // Sleep for 5 seconds. + Thread.sleep(1000); // Sleep for 1 second. } catch (InterruptedException e) { return; } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 6565d5bad..92bacda99 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -79,4 +79,56 @@ public class EventAsynchronousTest { System.out.println(e.getMessage()); } } + + @Test + public void testFullSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int sEventId = eventManager.create(eventTime); + assertTrue(eventManager.getEventPool().size() == 1); + eventManager.start(sEventId); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + // properly. + while (System.currentTimeMillis() < endTime) { + } + + assertTrue(eventManager.getEventPool().size() == 0); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testFullAsynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int aEventId1 = eventManager.createAsync(eventTime); + int aEventId2 = eventManager.createAsync(eventTime); + int aEventId3 = eventManager.createAsync(eventTime); + assertTrue(eventManager.getEventPool().size() == 3); + + eventManager.start(aEventId1); + eventManager.start(aEventId2); + eventManager.start(aEventId3); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + // properly. + while (System.currentTimeMillis() < endTime) { + } + + assertTrue(eventManager.getEventPool().size() == 0); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } } From 622376e0fa3b1723b86853c4c8a882b6bd378cb2 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:16:23 +0100 Subject: [PATCH 09/12] Updated version snapshot to 1.14.0 --- event-asynchronous/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml index 60ab8f0aa..1bc4549d4 100644 --- a/event-asynchronous/pom.xml +++ b/event-asynchronous/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.14.0-SNAPSHOT event-asynchronous From 8f1758c28f663f73ea79be8d2a1575fcc4d9fffe Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:46:16 +0100 Subject: [PATCH 10/12] Alter JUnit tests to run in lesser time. --- .../event/asynchronous/EventAsynchronousTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 92bacda99..213439203 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -84,15 +84,16 @@ public class EventAsynchronousTest { public void testFullSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int sEventId = eventManager.create(eventTime); assertTrue(eventManager.getEventPool().size() == 1); eventManager.start(sEventId); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete - // properly. + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to + // complete + // properly. while (System.currentTimeMillis() < endTime) { } @@ -108,7 +109,7 @@ public class EventAsynchronousTest { public void testFullAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int aEventId1 = eventManager.createAsync(eventTime); int aEventId2 = eventManager.createAsync(eventTime); @@ -120,7 +121,7 @@ public class EventAsynchronousTest { eventManager.start(aEventId3); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete // properly. while (System.currentTimeMillis() < endTime) { } From 986c529eb67e5eccd999e4bc13d268a8c5997fc7 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 21:29:03 +0100 Subject: [PATCH 11/12] Moved config into a separate dir --- event-asynchronous/src/main/java/config.properties | 1 - event-asynchronous/src/main/resources/config.properties | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/main/resources/config.properties diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties deleted file mode 100644 index edbe90e05..000000000 --- a/event-asynchronous/src/main/java/config.properties +++ /dev/null @@ -1 +0,0 @@ -INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties new file mode 100644 index 000000000..7216f665d --- /dev/null +++ b/event-asynchronous/src/main/resources/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=YES \ No newline at end of file From 70318123fe762d919eda221e08b0f7bf2db5b8dd Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 22:22:06 +0100 Subject: [PATCH 12/12] Changed config to non-interactive --- event-asynchronous/src/main/resources/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index 7216f665d..edbe90e05 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file