From 43da0559948cee02654ec92093ab67b687652f5f Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Tue, 19 Jun 2018 10:00:25 -0700 Subject: [PATCH 01/35] add unit test for Dispatcher init() --- .../struts2/dispatcher/DispatcherTest.java | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java index 196b43766..cef15345c 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java @@ -47,6 +47,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Locale; import java.util.Map; @@ -134,7 +135,7 @@ public class DispatcherTest extends StrutsInternalTestCase { assertEquals("utf-8", req.getCharacterEncoding()); } - + public void testPrepareMultipartRequest() throws Exception { MockHttpServletRequest req = new MockHttpServletRequest(); MockHttpServletResponse res = new MockHttpServletResponse(); @@ -175,9 +176,9 @@ public class DispatcherTest extends StrutsInternalTestCase { } public void testDispatcherListener() throws Exception { - + final DispatcherListenerState state = new DispatcherListenerState(); - + Dispatcher.addDispatcherListener(new DispatcherListener() { public void dispatcherDestroyed(Dispatcher du) { state.isDestroyed = true; @@ -186,21 +187,21 @@ public class DispatcherTest extends StrutsInternalTestCase { state.isInitialized = true; } }); - - + + assertFalse(state.isDestroyed); assertFalse(state.isInitialized); - + Dispatcher du = initDispatcher(new HashMap() ); - + assertTrue(state.isInitialized); - + du.cleanup(); assertTrue(state.isDestroyed); } - - + + public void testConfigurationManager() { Dispatcher du; final InternalConfigurationManager configurationManager = new InternalConfigurationManager(Container.DEFAULT_NAME); @@ -208,26 +209,42 @@ public class DispatcherTest extends StrutsInternalTestCase { du = new MockDispatcher(new MockServletContext(), new HashMap(), configurationManager); du.init(); Dispatcher.setInstance(du); - + assertFalse(configurationManager.destroyConfiguration); - + du.cleanup(); - + assertTrue(configurationManager.destroyConfiguration); - + } finally { Dispatcher.setInstance(null); } } - + + public void testInitLoadsDefaultConfig() { + Dispatcher du = new Dispatcher(new MockServletContext(), new HashMap()); + du.init(); + Configuration config = du.getConfigurationManager().getConfiguration(); + assertNotNull(config); + HashSet expected = new HashSet(); + expected.add("struts-default.xml"); + expected.add("struts-plugin.xml"); + expected.add("struts.xml"); + assertEquals(expected, config.getLoadedFileNames()); + assertTrue(config.getPackageConfigs().size() > 0); + PackageConfig packageConfig = config.getPackageConfig("struts-default"); + assertTrue(packageConfig.getInterceptorConfigs().size() > 0); + assertTrue(packageConfig.getResultTypeConfigs().size() > 0); + } + public void testObjectFactoryDestroy() throws Exception { ConfigurationManager cm = new ConfigurationManager(Container.DEFAULT_NAME); Dispatcher du = new MockDispatcher(new MockServletContext(), new HashMap(), cm); Mock mockConfiguration = new Mock(Configuration.class); cm.setConfiguration((Configuration)mockConfiguration.proxy()); - + Mock mockContainer = new Mock(Container.class); String reloadConfigs = container.getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD); mockContainer.expectAndReturn("getInstance", C.args(C.eq(String.class), C.eq(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD)), @@ -248,18 +265,18 @@ public class DispatcherTest extends StrutsInternalTestCase { mockConfiguration.verify(); mockContainer.verify(); } - - public void testInterceptorDestroy() throws Exception { + + public void testInterceptorDestroy() throws Exception { Mock mockInterceptor = new Mock(Interceptor.class); mockInterceptor.matchAndReturn("hashCode", 0); mockInterceptor.expect("destroy"); - + InterceptorMapping interceptorMapping = new InterceptorMapping("test", (Interceptor) mockInterceptor.proxy()); - + InterceptorStackConfig isc = new InterceptorStackConfig.Builder("test").addInterceptor(interceptorMapping).build(); - + PackageConfig packageConfig = new PackageConfig.Builder("test").addInterceptorStackConfig(isc).build(); - + Map packageConfigs = new HashMap(); packageConfigs.put("test", packageConfig); @@ -273,14 +290,14 @@ public class DispatcherTest extends StrutsInternalTestCase { mockConfiguration.matchAndReturn("getPackageConfigs", packageConfigs); mockConfiguration.matchAndReturn("getContainer", mockContainer.proxy()); mockConfiguration.expect("destroy"); - + ConfigurationManager configurationManager = new ConfigurationManager(Container.DEFAULT_NAME); configurationManager.setConfiguration((Configuration) mockConfiguration.proxy()); - + Dispatcher dispatcher = new MockDispatcher(new MockServletContext(), new HashMap(), configurationManager); dispatcher.init(); dispatcher.cleanup(); - + mockInterceptor.verify(); mockContainer.verify(); mockConfiguration.verify(); @@ -363,8 +380,8 @@ public class DispatcherTest extends StrutsInternalTestCase { destroyConfiguration = true; } } - - + + class DispatcherListenerState { public boolean isInitialized = false; public boolean isDestroyed = false; From 31cb2ce21a604ddf788bc401e45882e3f4d51a8e Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Tue, 19 Jun 2018 17:42:26 -0700 Subject: [PATCH 02/35] add 'com.opensymphony.xwork2.ognl.' to excludedPackageNames --- core/src/main/resources/struts-default.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index b0fcdd81f..3896a51f2 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -59,6 +59,7 @@ 0Qi$ZKK2C+JpSyCh(=e1-BQzBb2JoL`}H@!{CVaWTtdm>{? zHl}9dYR+#yktD%D!^)jBlcPAUlF6}9mpH&Cl?)_ zBx8`FqZXn&0R3IbK!j>gzW?c(>reUDa}WCGt(~LUzaH~|5jC`|8Ld* zx5fV3c>me=KN|SotP0To*p@8+w~_ouLqc|T&Q8vM)>;-|VXN#6aCA0tq&Kn#I5{P$ zjkuzSqjm*{py#K7g6|uU82*ZfaIuF3icIbGCnUx(3KUF*r7N>;`q`dz8DGaj5$BoMJTCWCb=m5uxvZGY@%ws2{U!OHYk<>VYrUTE<)ZAQil}N;ZZZliM3)o5~{80@i}|jP*!+D&4L&I{|j#Y5VgCO!ztz zfNdDniy=SG{5)I*jL;u?K@AMad_IXuo>Q6ZwBB8IB$Y`NUw7+iq1FP&^%&)=$chV2 zch?gj#RQ7GV#0}@GiEKqL1NvnBe6giQl!fy#Y46Sqpvr47r{t7r-%qxZmBc#A%_k5 zpl-MS(U-$9E+kfyjvD79+k)k}XH!}w3>JzB-%g$YbFt`b+F8ggH#7^w9KHc-d1s6n zI#ZEb0(dk~!4-`94RyBYoPLY{)H&}~qzvGRG=hHBnwh1J*$Zl+Yp~D`X&z+CCG4GU z>g}N7Lkq+tzJ<{lujC9!$vDK!hiiSbp|@2ECg-p#nNV(@kVP62%uHm)1W2&Plpu|w zON6g5%I!1;U}(*|HkdngrcTAK@Y2J)ysGX={XsGpiRgsB{9tD047A^~QfT$^R$FrL!Sq25b!Tg$|x%NDG7cs3;r znZq0vtG%E^WU581md^@_k0Oen5qE@awGLfpg;8P@a-s<{FwgF&3WapWe|b+~Qkqlo z46GmTdPtYCYdI$e(d9Zl=?TU&uv94VR`g|=7xB2Ur&DEid&R2 z4e@fP7`y58O3gZ3YBCQFu7>0(lVt-r$8n6^Q5V>4=>ycnT}Fmv#8I^>?86`ZD23@7 z`w&@OJZk(3*= zPPd+z8{6G;^$O<=Y{op-%s9ZY9@nEJm{crdmF%hD@g)m^=yr% z|54{_3-KF`QKm3KVtNN&=?hg%$CF9@+lh;(MG9&`Q^$3cbnFf{#>t!C-*Lh0^81hw z*tc&6(Er^w{m&y>`LB*>5ff8@i?y?eotv$-9l+SckyP2k$=Sq4;XlpipC@+@K^JFp z6I*8sBY?BrKacRLL|r>%LDY~fkVfg2WhIqb-=@bgT@|%1=H669Y!sBnXw~>)b!AMz z1hcSdDDjt+opnJt|1ScQOdu6Y$<;{PdMDGvOphrRC)1~+8aw`PJiW>gP<>WqT0m#@ zVi^#4t^=ae>XmB;)XRqi8Vs{*^$f%#={h#&aE24y9a7jW@E+ElIp9gzwoZBd;B!h` z5=gfMD@ZV)OTAPCfJYBXp^t#L`}gles!6h!#NlnQri{`WmB9f$Cob@9p2P4Ya=#ah z14Uhmg}CwMi=DZnptzf)MHx_%wRNuQIWMIbGOvS`5EprS9^Lfk0!QJKA!&|8iX4(^ zrx)9`Pqo6HnAGX33$_X6f5WSb%QOZcIf8T4%A~fKle_`}#wuh7EYKpJw62&MA5UW z+TSwUs!A-05lofa$w-;8Q7Gx~thha+iB z7hj>ber`-1$l24mvADf~y7laCGF|$8%FD_9MiX;zO?%rK7}HTGlBSn#O?pUp#Q>1|5Fbc|1CZI51e4-hpUR`OTMy^W?f=Y z&zeGKE}eUE*pBX>C`-d?F-u=4xnZN!40LAvWXxjXMxK>sqbvdh)`^OW#t>$xSQimd zn3o~Z)p-Wv=L^Cgs4wU7r_M#Cc!%;@E+0x%nBY@>}iS%v95BZ~9`>T)BD^nRU4hGs9Y&d014mu`9>PhIMC?@S|<=O@@z^c7WTMaVEX6Fg@F;36hBCN%+q0bSo z9l$`aJ=-xDWhjs{*YGQ(xTvNzoAQ)1409|K1D~Ww@+u+#WDT{%i$+p3HbB{pU@Z_W zMU}tUo?~gqv~c4%!R1mtF5-j0V=LIkl_iQ3zU(0l9bww@#+mz1EKfM^|7HEtpscZgWmpIjM%Zy36R#qH71dg6^bUC$2dMGDG=e z&Tw(co@DXa+aMz>FtGBUV_bbj4TsU;NDN#%p2e!cPIspAD4bP>j&yZ~cWC8W zT~X@24$2%d@?e+jym^~GW+e}+!js{Z`0*Ea_G+hq7Y%z%xZB~wPKs%A$Ot)?=1Y$(p9Go)sY zVF|aF(4{>AySwb0(p7oP(t!u=IJ&jE#FskPch~R-yDfYW*1?91u8U4(Gc?xJ{T3T- z0WAiuU|AFvIY%dps)x^qA*{>?BsnVS-VG-Y4t4tMLLgXQRDGOh^g{se5_p|k{a z2#uG_3-f0Ww0zQMw~UadQtdp{rSP6Yi#5DjcX>#NB#itBj*=<|xMs(kESlOx# zUNZ2UZ{NbbRpp|~;_HEJN79u)`C1hPzL76$a<9n6eJeb*9Y?@f#%uFKLs%EPqjNS(M7ysxG}zE@u)9N?a}QI)fBZN`>nbM*o)@S5 zpj-mF1ot@$@KkCjsEHch6f+3F8Xm*sTAN#I38ER3i=*5 zkkEYx&lBvxpO>JWMe|iSkyS`bgCa$|tUXjFa*RHkrky%E{kDRZnGqH;>dua2;L-ra zh8?zFV2NeQst}R{*^F=f(vUoz4&J{svxIMJ<+*?f+Y;*5PsQH#K(9r-NlpLa#e{ho zYZ+}LYto4bC)UK=o$k?CwzKN@>44{j;<=B58U=1A90@-5toCJ7`eD+EwD9E$F&U3g zgz?g$mV5M}#M8UM$TbXArno+K>9PZADD#CF>6mKbkqL%1MCC~FoH;PZ8Exiq0WGw-$QpSOqoKL{7Vu zUMo^|RjaAn_(0x0rq(I^tggmEsjUfS@#OW)x5aJ$v)k_nA`53A!EE5@bL_5ol$a6t zhI_^pIjvGfJvKS3@2<8@T#F@I|5rYpY>eF0Fi#x`KUti-=;nbFv19a<2;nWv3$&Oo znSS2yngi+R_hQjE7;Kj4c}saS;I0!HMr;`~p&5nm1!4=%VrSB3T0$S*h}b8p-q(s% zc)Dnz&Y33ITyix66dOfKmdq&j(jch>~I>F{QfW!}EHiN-fBQ(E&&K*>Asa^`mFO0t#>mg2G5P67i-zMPx z%2-qVrLq1`wD=DzEgI7c-z$I^@|BkuALsrJ0)w7?vWxhq1ZmKlB}HS|hN1Y#r zQQ`%`%10&$tUM%NBq6_6@3#n+I$ehM*oekdaj3Tfyxt655V;14iiSw?yr-`xC)%bN z3>140(c^cLDCu@NLKQ{y6%n@iD%UESt$Q% z8YFF{}I#3(y%blS#bG`VV%W^&gK}Yr(-nzHkRD9I+QHPJXB9M46KQsY{Im> z9K|MoyUcPIqDea@AoPnA5xFn9(REe{88-nGn4GbmgizYTd@i`!L3_2a$RfR1TWYQ= z`Yns2BYEK3Xmj1|s_iKAE$gBC>iyoT21J7-hgpHRbu}is`L*D4M_A2j*>66gF=p_6 zrWDQUB76YlQ{i_6mOa!V!6U&#OUV1rnZ+y!1nqt(K^yg_=E>g84TyG6aM!ET73S6s zGqWxK&&iE7Fx4)PSAP*&OsosU@fAy&DG9?^{=~-h(rpzrEkaEB0kF#-yy#FXpFeV| z-P9J^nMKrO+QdG>g|lv2(fA}xz#bZ|&KL^!7jL6`B^c`@r@vU((I7iiCMzBxb+j*j z90*dC%Z!UQ{*WJ5z*%D5|(6%3Ngj3bSo!HHFN8$aiwtzA%n1W(~VhCV(U3HnUQ zv?GTG1ew2_YwgPnHF$&=CG!JZkkosl`S-kqPyAL*NjcM_UQh(NXX~hKdU7|~=`iaP zb)V`0H04$fAbNr>o84__2-QQ5AWM+xTM4WvE*gTEVpT!qI57A!r>t4kdL1kw}wk0g6rfK=GQ9p3^bW;O3eQ_L~E6 z&^m1{GJA^QwybrUD-%Q=zJB8oq=}Qi&|k0SF}LDjLog}YtHwk)nxSBA&+bCY`uZxN zgC%;j>5F#Q&$X-8^Typ!oDmNkJt`;EiwP?5cuRXZ06-D^`mpx4XxFgQI`7(csZ zYuE$g`wLnV>TsCbJhRd%VZ0(9zP!F)**Oy}sxt;%3=VOC#_XY7&&ydw_cIRo2wF_+ zTnbn0_b(*;9pw6g;wDD0d5lo&o0U0=CRq^&ik*D!84lOA05D~NSpmJ!*6^V3`U{Ek z(`bbWP%-J4{YQBr0XLWStW4F; z1k4T$d@`TCL4(uHn!4x<7>?&7;|XUU?!SIPm4EkH7!bc!G{mlpAuApd9CEhh8OU5M z3Q?Da2w<9At#hd9d#DYMt#GplIOoA^5grLD;u0Wo9~huO8;xk3Lj+YlU_y!I4&~a9 zeNrsPk!L1?6^nr=P&~LADk+QQ0C*)0Go*8dE5n8tBJay;oY#7wU_V!G*S}-Al97ZP zERQY#arkQ58-%`wb0`?FU5&OsOWFNu-rWq#x`to-8N`oy^GdSU1_Dv#9@+Ayk;tGX z@PGp)2CR3M>c@$M{Zu^yGMAsWr!K=2J;h`wcCN83Z(Wl^kVY4 zAr09~9+!<(S(NKDGmvs^(i`8Jbj)W8M}eYM^j4+8i5Y8^mf2hKRQlsc)*Flg@zedf z^6i_`sk+s-v>?IWm?SZ^w9y1SFcn2PhWM4o0UbYhO2zC6L zzZ+uBlWsHGsqAV^o7^3aOAQ`SfaFJvMe=f*laO6(!*PAKVmd~28a4R7Cw0=BQ965m zok8vk(<9524(gJ!=TY$}SMy|-_N+Sroz&~DzQ{69;WNHc$V(J_n z7wh>6hT>OgO&xGU^qRqo?zSfnb=YfA$mY#zxIKl5=7IjfJU zh~qP!nWIv_roGE(w}x$a!fe^*LHt}I&b=gIeeD^is*rzrzr*ct_l4cpeD~^_q}~() z*9o|V(U#>qVzA#YeynG4Vpf}(0e&kDY@<&D!wgx`ui!;_R;trA zXtdYg_^$y2mE4)R)|Inm6JIqrc(LEz*C?W z??Y+*)(t0aPYQmdp>lNy~WL+#?*?Km6;XktG1yW~-d5pu@b3tju zm7;va>02fu9746Ru^3%DMLRfSS*0t8=mx9a-FX1PvYK>Osc!esNDbjWhTc-#{8lL& zibPAJp2CYJE5*u1rbc6l>?;D4;1G@kxX@}3wnR%Av-CVtCViJp!y0qu6P?FGr&uB# z2jCMBC%7f+wyY)%&X%#5P#VMca?E>Rfh}o{+|@1krtBxoMcU0=KZfVREka0#S~2-V zDjJB22hB+12>pz01`_&DK|{_7Ti&^r+nY?OGsHbjO2~gOoE@VpyFw8$ySvRL`%9LU zhF`>x_Nx_-s*mQvV%3*~IRW`owOG<nw_;7d7mm zg2;rCdk#z1UYM8yrHl$#6pBQ3JWl08!0xlx`o8eyMvlUTEG$-ULa7V_qt1K(mW7X% zObCeYhnAF+Bg#sU6%{HD3QkVruofSVM0Ob)mvm=0jj)?f-{?p;WmOf z;jws~rV}P9de9vw|MzQ`wx=g#>^cJirei*1pg1(UkI4OLfn<(Xo0)3tWmrXRYjK@~ z;wROQxKKCb<@~g|LL5BjaXE6YmN?GBygjVigg>@<4(hNww22bta4TCPh>LLFjK55G zw$T<@y{?A}?72b|YxKqRx(d`*c6o<*d78+H9 zkph)*(0y|wX!VP2qXTljKkhpmgAtNA-Gxb$36;*8p5CgdjstX3(*c!^A9Rac{zl23 zY{IcKxc1Zz2+FeJLQY>b>Z8oBrORrUl3F_ns&aVyDk?Dklu06iOPCDHjUyydA=?dn zEXO7+YU;&H+fo;K!WBJ5qf8;y=rh#Ad9_RkpG#7?v#{y~JrD4Srlcc>oNXL)yC+T| z{K7abd1wOZv)lknUXX@p9loiMtkKpxpyJ8*vxyfgy*Q5 z(-fVWym|FiR(p7P+3h=hyV5F3-dHm!m7h>N74uUw>N%rvJ)FUvKVC(LMdz!8}etxgT#j!ZSVGNU9j>JLgHFaIfYDLh#{?`7W6ieX|?Ssy1?1@6Z zZR#DnM_?G5dYlk!EtZ_GueObT^6STXkRa9oK39}B-WFH(c`I#a#KpVr!CG2I zTT;os8CH1_l9>p@0y(hAY;`^dYLSp7`Iy!IMxrDSO*+{L=svXTuQ04I0o3Ves?arg zXCDBpu2K0YoHDrd7T3%Bl9-v8}V4sbA~!b>K-~{WaACD07SZ?XeX1ki_}WlQP<9>$y#QlINnU*(6jo!jVk=TKxP8r z_JhdstJW!9)B-Dg03a;;cEnVkwky_9OENsPD6+ zUV-YG!g@3ct@I`KS>7`EuBg=sv11g!%W&04Np2;nb%0uUq%zuD=fV#iS4 zm!>$+F!|(#J_-KjS&xL*=z#tqqafn{m1j-%SDv+uotfExxYfbRYqoO&h`bqv&3mo3 z>B#gzT3S+)!1Fq!dRjyxs-%UDqM$`e`qM+S)inBjt8#-S*I1}!g!s?j_@J52M7rXL ztyj3YoerPJ>psq&VspOX?}Wzy_Y2YTh9b0fFl5Fdi0|s*zWdZC5S*`KiYm*Zq1|<{ z;kL(z!jih6$Sc12kyuFFsL+oaco?oCA{>%rdIU?FoL@6x>-<)7#9#~ zEP(UmvTl^xk!!sJlzh?!r$QYTMlHj`Ha>tNIZ2cf#Mt3Lu6r}94x%PzsE&pkX{_+G zn>ZxIF+3j`_Sl&z(V`^+cpk7cp8kOM$VBfWx(8zd-74r7ZBO_JQG3)x`C8N~!quq91I@b&j3C#zgJ;QbHr$p+-F)QRD*)JgVlWGMB2 zaE|^)MfqoLNdv+i#|+E&Yx!nm)MUg3*{r+@W$jjBZg!g70vn;tmG=hPR%j#AyP4tV z<@(%+TyAAORfj^ZHFRQDBiPD(BUME(^XR5mP*5RZI*$J^Cg&yDZZ z)5g==&hS+i!7n|<5`!dxXp`8`CP}*Qd7*o&iMAmnHa3n*E&aN;Ct*+1MOeiFhW>CA zjZ}2FbK^JmQ#UA{^GM6<$QCxZ=eU?Bmbeklv9OQguVSm7?Zm+TlaimV zh9q4+yj?%L{da!G{I31AYC0yvnSKImQCD~wsBh49rY_8!w+4rzrc*NFjra4CsBI&( z2~~eTbd_!1$Jm&1c4>Z&;0BQOozZ4AqZzTWmJ|3t*La6ToTAh zCD&J!sqn_}g1r=S4|(@OV^i86rX1#31KM9&wNeb~Zpk9m(~a3zrv;*Mk4g9TcZ6jf z(FFT`L&vc=(&I=j`z*k$PXcn@wK{dQ5a5uh?k~F_4g*BA9h(_(nh+z%{)eQIOG}gF zu~)LBUcnh9Hd zTXCEaMa4eOBpvS~Fh~eFzDirAyVNp1obDW@!TC1i@;X8t;*j+#Msh;#SkJ>)RLh2D z(>zvL(xjJl|M+5-yzCmYTKyW;u{2H)jilAzI!oqzbRDLqa#l-^sYJW8jwmXrQyTmC z^ee=Kgq*NEr6ImzLtK<|G_`oR8Xl5aX?{G<3M&UsH((|(3b67N5%#R$-&DNm&a^_f z5L~S$_*9luHxd0^NCy+!_lenNnCUas<{AEY7Ve^VS0-ybtiIc6e!+F1Kmx2*+JR* zM@)T28BV>_7Ea6=Z7#TwP{b9T}gxiLzH2w^>2t+H)UP3;%4*KeU>2LN+y z6b^FasEP8;fRFx=Sb=*k++8v(~AxraTCt@;gk=T8SQI;U|=x4lkl ztbFwOL-xkCYg074UTqWM$id1J!Mj39wI}x+dSBIwloR;i1*sxCbq9z|qS{rPb>N?U zk{W6a6}GJ6UqD!|9V+YLZVjOM_?f_TUnJLqo|fnce9)U?zO_G4@jLZKpI>x0e@orU z8QMl2_LJFNBd}O?-uodrm>$6!}8@DB-7KK zDEemFIMb2$JU$u5;O-9l+=x4<@0^ex^?QRqm9=i!j5zX4TW>fQmU`d)h=?5_Dq_78 ztM(Ndq&O(=Td<{*1I6F}6PfCVny9|tnZwP&_*RF4Q1ML5C%$g&!(1%-pw=%J$D>|( zj-qT%%NIz+kKdbu>irXrhGrUf4mp#&JF3S02O@MRsu6FK#^${H%=>tP!Eim?ku#@$ z$Z1cA9p&?PvyKBYRd1B7Tl)mFIA0nIaZUR*jI`g~MYmVmUeMiRD*!4iw5?%;PT{c3 z?4qvBw)y$2YXf}>v=2yr#p^wf@5M{1@2LDnH{6Q``fvF*7o^uyV9lmTXVU30NJ~!O zdw0)8q?a}O-l>5fzk+OJy;xvYUUA;#dhIY)|19O3NArC`cRZHgeu>q%$(-D~=Aizy zx{_!QQ`sQ02SwV8^0W)zyX>|?gK2s)3hshtr^BK?BegR32!dxEi#nq&is0mVFVkdx zFXaw*HQBwv!lj66AnOwXTI@~^tN2T+Shud`4?A%fcZD$fBSoq}U!6g}!!m|Yn2`Y~ z(QC$TI*hQ-x#EJXQG-!o721T~E--gQgc50ZS!34x+bDegK0DRF1&n;W+^qftvDE_i zvQavZUSHUmECw;=w@CVGBG`l;sPpCJTS={C-1}<;CT7KjU87wSggrdv9-*>(T3odS zmkb!Kf~X|Z3*a0_k2r2qmrEmlP#T>c1SKCRW`D=m5^du_^Aaa$^Qw@y29&b?)PqgG zv|vt6oi7+l&5H$xV{zBPR}O5(Ux=0rRcFWt?^&j9rZHT554X$XQaz8Om|U1iO`7%z z7``7hrIF-?v0#_4Z1fp&*3y4gaR%Zl`0a310Dw+3*f8I5=;g03^(HTH* zEsB=CT^(TQYL*!6f!0|KKe2s#-i++VbZo203&ew@eytTjQ;iuJMHq+g+?9z|`uZHRcKN-OA`czY`ftNn`6E((Bw4wv&l{V^w42>+0 zOQYYZ)qyjvlrme;5xykE>}DQ|#|L~WvwxzW#oZQqYRq#@;Qa^UM_G}di%1QS32YU# z*NZb1y&0~$A;F*Mx1<MHzRkvrCmd45;Q9-7X>Si$!L{gc-_YK&M?w-H*^i5<1}xAaM_^`Wz~cFQv*ciyj_ z6A2q#%HWow>q&^~?1nT2c11SG>eyelzf>uQi4HF5=aJ20i#jUU?6Ky-|GDa@Qt9BIOs&OCjXmd>p_`+`Is8R{;7xt40G*T8dvv$p za#*^Sspyt!$>ZY2*b;wy0rayEL+RNPdP{C66wl3&4#mN@)fK!aj@%dTSs2={9Z!4T zaC>I=O@UPh^)zR2%j~+w$wL2=m&AUNtqC89Xg0>$1*R?5>Z5S@TeDG^0v=!}gr!X@ zmRONA;-wMq;iQ8(F=C;Q<`P~f-t}2gN&4{P`$}t4BIN}nZ;;Du1#{iv-NEv8l*X1O zj#M~YlgVyC;_|#|%Fh*Alha3xI~!5an-yD+D*mONu63+*q+X|c3JLtC_NoFb-F*P)952%A+VE z@;18-9=yJd7}ziX#2r#^2ZY>Oiu z>R}uDhjyQjr=_u&U5;dDe|$g~AY|a<_EpF{88RVfbw`EniWJ`<(20?h?M>w$6YRI) zHlviaq-%Q*TE@a872%Ht84${eWQH|j_*o(tmk_$^;=dM)1sxP$l+*f_AitQd zepgE0M)ygw>mr@cxI1B4+fXl~-bCJEHnAOjPiRU%70 zh>bay^YOHjckCGf(F2OglwKTotffCxYhj5R4;zEjz~v)N?nL^|xa_)Y8Tq-+M|QvB zALvUtstjByBkgaABMrF$@ybZcQxLv@r%$al# zFvlp0B0RO$+csIY#P>xVA4xb0Up_nXwDvXGrO2=4^!di1a@Z>MOt* zX{y-Y1+NbretZL!=Tf8f!J85|`kUX5Yd0m?@yF3}{!2%T_J6G=|M0T1)L#5ho{)U3 zq?2jUfuU1Z4X7taGv z=E&o5IP#tlJ_=U5HAmuYMEHvNCEhkRUM4#|?o1!wuD&{7*ncEEtACS)meX*hFGFh_ z56IS;Pj+VUm|KJf+mMT~x)jRUJC3~b*nt04V({c*BPo5z#*%`Y(Nk@v17>s5ot8IK zF_$2Wq8>UtE38gYLatPRffgiwI+RdtliH>S#tlI`=fF0XHFGP<8>R+^VB?T$u=G5z ztSk(otg0?p3Jttq=Dg#d>FVsYtTk_;8*ZdA0wbnp7M0u(V$php#wy-niuw#*S&1*i zg0FUi=*qGk1~@Gk9Q4@8o=r^`Xkym#6>ETNtKqwEg9#}h{9e!Ni|H=!%#v80rbc0fi$zIYC7$Qu57+DQSgSPDqypm3$IcYcDk7y?6_Uvd5KS)iP8Zzi2!WAO@;YM@p zk(){lzs(3ka8bT*dTQ(FNi6CI9aGL3vIp&|!h*9LDzA);BW048$sDF5n08c zCH*>0r_O;Fn~XB!<+eU7sUyna8TPB0R;ZQ+vKWWc-JtmD22nuCzrF5P--#sJ)nEZM z{-)A~?*vhN*UZ~D{-RwU_nrX6mT;=Nr8KL!=k`Kicb(qPDzy($lAHyb-noihYZ9LP zSj5S_k#E_{^TTKe)UVT1^xE;wxE;+!kV$%WIze-oiQR^4msX&D$N-%Mcyl>_mC0iq;mm z@yW@w_D_GrdI^Z!nz8QHnS6a{Q^9uiRw*-iIIBq^#3i)nSniR%7)ZJrL!_W3$BB9j zHeX77JB9N$oA9Wx2-j}pJ{w21F}%`%1+XM}>-b-dclZ0|4no805Y?cfrP6Vgga+dVPE!x%7|K});=3^ZKa+K3nHfyVXUz*JF~rg_I=xKqN!K`A#T zP;Y2pbz(*hpT?HG&9O5m^o+RPW-?x4m#k1?@HCe<2N)Sc9 ziD82t!|lTBQxuYKDc|_K|9F_Nf``dmup8O82f&xcro57hGJnzCn*Pl_k`crDpFW}&;~Adzx7;od=v*WX8nmT9o7spI>wk`Ap+ea1&vFy z!a*HU(2@GXQ73SUUFH%!5s>FQpFE&twM4lK#>{t!%;zwrBskf9M_IW9Bx*^TR-C4y z`T=r*ruY;YGw}Rc?iky;C;^=aHmzH|1XF@K5HC>>OrKXf8wH)zov%hFLHc(xPq+L7 zG{@_qB+J7|T1-MXk9XAYo2oAM{>g?o$PjhUIOa88D+hwyVhqDG5h&Ru%@HmO36-G9 zKRAB`s^)x=+57u&qch|+M3J0mxM5L<8S&mQ8=84rNsNzHh>yBk!jF?&(93m_%jW)U3(P+my7ddRAP%7ALdmWJfo>t!a<8)+vaBgo9A#Ai=>I}bH_O;dXz0!!QC-(qQEFF?BZ6J8+ANwQq$UZ>zj+3BM`XZ7e{TisCZbFy;xT@c~C}7xl;2|is?rsln()-LQf}T?JIC^=6!W~S&?;cJiD44${yLLg)hdH>0^PZc# z^!0|>BJVEH=?S=UkB?l8J_85$oBH#8Jh{cfqqeXac-!}RX`<|PkAokVz3M9ovFwzpLrJm12A51(9n z3ms6mG}DcYaCLp@8oAzIQK5p%1ZFba)6JK*V9FR+q1p_>=eS>H4v8qWu6Q* zWpljPjXloyzCcm}<#+e^h4*z$T4J9Q;3xF*_ken+H%$)zAI9D${9oZW_P;XB|MOCZ z#Gf4fe-YPIHMLRHF@0k}!TVbCN(Dvd^ARBxk(xj)77UBvB17^OI$(EFVaZwcjEScw zE-Nln?e6==Zh5-$yC92rKvrFmDQBOQPRqp{F`R_9QrPwa49=c`sLa+>6I`SSnW%o!Op2T_>=fqU}d(k$39S zxUil;Pr+rz?!mz9L z`O80EAuX-bn&!K+b2;tekg}_ouFEe(nz5s$5Vwlf_b13*F`a?OH5A34vGP$VZ0Pm#)3 zbC?YlC9}hkiJVsz>HwNl6#Ir+j8z1zS)I{2$}lQ5mDSX}nWnZz$gNePmGT=Q*^UHXa+WmknM*OpuB9UB^Csp_T=VUZw7Vp-Nv|ZP*9w zM=~pO!FXf{*yLpNCc&Dykw0EhHmyt%UQ(b)ZXIQv1ja(#7LWFa+zREU`Vjp@eONhj z1*0t}Fd9dqJTZ_ULVAHJ51G6Zv`Y^lPfGflxL?+IZuWNmt^q8|vi;0O^ms)i$#QU3 z!C#ffBy#fAY4NEi8=()qp}|%MU4Z{SilRomY?tyFd%h*w&)cfak|($g=CY|5ZT>6K z?5%C_AiT+y9E2n% zPkqQD)#fz&D&FYMGxEJJfu9_>xBNnLP=A3Hq+C^=S9zHkSV`$tM*qt+G_iaJxLmM_4gD-9Zus;LFv`r4C`OlRWTd4wiU395bXO{4uN<}=o1(E2F1Q`L~B0>v0ItgJ(r^GbG`?>c!r^Shu5UW z)yrPHk)m)UWg06M6aOysdam9&UYodcYWfO<)dT-X?D>x~C9i9j{XH z&&gh_A8u6JT6uNTY93CBb(lFV)sABl!@OYr{I^rDWi#7ZMxe+Tc}ZSqa& zZDDWJ{;IqV>uy(_50zdUZ*`7f;r!b|4a=>ZR=1HDy&wePLE^VaC0C&eadk`Kc$z}Ksqxpi{ zsv;9dKUIjBtWz#rs)I8JZg}aNp~&1v`sWZSgA)TUYvS$nP~rUf^<-EJEsX?V$c{0S zuK?aG(upOn_>+91Jf29oo_DfIX>Hl#RJ z29GMQgU&xBrqC(4Vnoc{BG9U?0X5~7V|l9=n&GQ9Eoi=bIncW$A(-4ph)_rmDK3fecQR@rHH0Qqph}sk7pMgJx0U38$`CZ~^ zcuOr30aK8;cGN;d@E1Mk*|58*{DprAC99Rw!M`j7u*+*`DktQ_|>xZ##ES7Mos9 zOHNZ=ckhc|dR`#ET;DmuM4=6f+0v$OwLGQdWvtBZbqt4QZ#_1oaGkP!%pRO)*sBPE zq17@MC(XkvlQU#sqjMJLngfzIKj(kj`#sJ4{LJfB77vAxBMS|U_vt4wf+hx0eMz*z zY8&B&PJT>n3#d9cSESRP7dBU^mOYIYpq zGL$&j5HU1n+-OhkCc8cEE^W{*s zpD_BxO&6sm=mys~kj1DfPj2uX;wKjH14EhC zQs>^L3m!U)Y=ADvb?uBfiqts>jVPN9ja8JX)XgI)PKryH;5yuEh&?{(9!|CL69HCW zy~G6!^fpQt#!XVNvl5UnhXf_Gj#)~-E5+FhL*YaN`t?Az%G~{GG3;UdM%MahxQbQ3 zCfdZF4o61+)XQ) zhrIk%VpZb4gC@&OMP*8NFZ^)H5qL`D0#VSHShP{zJrWyyU7)~uj8KviyYIPvDg)uxE8Lpuy;eL zvIOB}E7xvMWG-4wFHfrwfnaB=-a_;(6(v_26FrgiwCij2mIOX2x$||rQ1B4OS`*ci zgKBwRtiKLe|(>(@+qYCrE zG>gY%(tsa^XiU3b!v8jiDWuFdgnXN1A!aH)cY#lMoT=(2ZyKXmRQ)I<`6eYS&es)iZ82ON za9PLcJ9}OO$FHrBc#Bqt#M5Oj>G{5gm^yW~Y;Dvoy$@exWAPpnQxqt_m-3w8?y znsH^NGgNb9*({cxy6Qkd$p+ss!DUPEV0&u<&ua5%{5wK>==#P}r53LlviXTXWdyfg zq=AH;TICrW$#+0Jad{hd`AsD96~tvDqQDlJ4Zd(u-!Z*Ob*qn^vvkZ_Bxg2U{Wy5W zYle;W-Ix3XgQ>s)HH-eD>}3C?(h-=P4VZsMC@S-siDpNcLw!6E3wFBKygVZ@3y4tW z=XTVSt_-2Zteo943i$H@u>g2_o&0cTA+tDM$W|~~*NL8f zL6ECBt^si;yyHdbDhpad>{;l{ejjR`%lD390#BeC!`sz8w=;}CNwbdHPf@S!nk3&n zVnuKaPB^)3I5!su$L*o)aa}ekI7{bx6C!RAVdwAh)318MABQ(;4DhyHkOOa{E5w@V zOHpr(G+&vaM`~`IAqwu;Xj0;c_vm9DljwM2Adany98E?WDjl0A*%=Sh4l|kAO@-ZE z{vfhkz>ZGNaHh3{O=J zJ0Zp4+!vsd&W%8g@}J@M-?2ri-qa47g(PtE1e6eqpb~3@Ye860#Z&rk7@Sr0F*d^g zBBu>`dq>*=BYU@3?~n8Xw!-I_fq}1=?G8f`PoPB095HqOEj(|Gqnl<~p+X}-&0hru z9cL4xhoq2wW^GSsi6`G3UNg5sa9h_i_L!;#oN;Q2hnPMh$y)319aU^j4q}IFH;KKi z-RcJj~L zIY-Rn?>xe-_#xseXPR`!;^YU#g}<1oT3;Ykd-zXQC{ek`VUQ1V_MPEyWW^cP!Kh1r zn!E0~8M@{cR1wp~>}XY6&Z`r6M8{@6!qX|>>w(zr!p-Y~_zva}K@dDKeh6&QAw5y@ zBQWh3jY;dl?SPl*bxP}FE|uH>LZth`Gw?o0cAx~?EzN>C<>wy)1c}Zi1F>0WXX#g_ zcmA}o{g@sqzjapnF~vOpOQCtlVXrRS$ZFVeUVoEb*}iq#nM}nu#j!EY{XLKp;k_cs zD*g&<6K|xK7ju)I4h3FXDLc@aT<4~+HE+*8@LayHr|8Z11MaU;&eKQ%d)${l8Wqxi zu5$jXr5g6%ksU*;zjyumukH@K|I?rG8~kMjW#}YmYi<42eUdV_G5#u{T)sTI{*Tf# zOZi*|gCC8XFycg_3mL)syhv58Z%Jc=VsUXbJyp(<0ROZH_Wb8cuRyZ!x#Ye21+LV3 zA>3?;#mf|pa3Xa+uM5qNm*e#FH1xnVFR#ycwP6u(Z)i*8j?y~{R@fk&qmll3Su33? zNKICW;%@a)b{5vmDv7qqs=!L~u&QupDl5@dd@|?)(YMrdVjJX#m>@!ZHvD@=Dp$}4 zV8fG{)Z|kuI*`3EuE2U_c6bUPG)O|g_h5vy9!*+QK-PXxydK(&3bf9+<3{40iJU#` z6ow#&=Xv`)^xVW~$&&Ahtu0)}*x@`T0Gpu`T#zff%g#1Lfk>1iuFHblT4BeRS!ju# zQiU3D;#{&U(qoQ#ZmiE<^$s2QYBIMcvsLV&;Dg9uUFSW*QbhnE8~X-djE>@2w7u^l zy-HC`R~WF%kH(lv>{0$1q3(35y0`Uy!6!-j8_|v@GQ@2VzH*#w;E!+S1>_Y0PNRHb z(IlyUnXartwr(^ARr{@%#GvKXk9ocC8hoh!hb4gZ|f!Vr2 zI-{@z?20413A_$M`y3797f17LNWqU`K$cs#i_X3xDa}Cp_0~yJjcLjlojFEUnV={Q z)-%`hH?Yl2z0C>bM@r`n_>E#O&7+PkoCw5-T}P6ZZHSIJ^s{FkZTFl+caGt2-uy2y z;0m&~v`v9b8->|pr7o}!oG?J(iW}EpBlaQdwJCo3k#f8qxedJXjr8#e5WwOVukNlD>cDj-@Omr)~`wb|EwHYY*#z;b#&Sl4)Rnivh9>Hw# z(6e0Mqr?g`$sTl;)hI3dsv>;udHUn4Yq>SzUX`r*E%BCmf3GF|F42a;XB4n5jRBZIM=ZOwXA`(Z08&EJ$bkn2-%*wRtfE8G{e+rM$cccy)lw^dH?cJQTl@J zziv*5|9?f=|Ml?s*O;qPvDCyA{^=89wMt~Q0q-A95Ts#Y6N_>ZCHK>RebKIN5s%s; z#TY^|VawTdU}yvG_Vm$biS{&*=g+CBZ(xrwcLRjKQ2`&7dum!1`|;#!HoNKc+wDqC z%{Q%)7=m>)6KKkucxm-D1w~WUKV@Bn3zf3y&=qDs}s0s=#6_=_b=i1Nmjv z`t<5)v=>!T-RUxDW<^u8oJFUpG=m#qLv}Fz;Z-@o8+@|97?)ruEuTCkE!8T~ z-yZzNp++#mGzUhK`#VeGeQWbp!EG0qzYLxI2)-{$7F|I1MXUTMY|CDz3yqYk>*C|9GbO>?)MS1;^l+5P`&q@1uhn6DP_b$=t3WbwRnIt z!;1lwXa=#(MxN{ADdFW;vt=Y9mYO!pRy71FNEE=EOjgngqo zvAb?7+c+0+LvV&r3F0iYWSLN_l+$5)oKvt?ou|AuZei!ObpjHZcE9K}9_aLRo`Jhh zi0i~{i>VR(&7ly2Vi}2_aAMglxb$3Xo^KvfOAJSbli{iQXtu(-{a9D>zviM+6QGEb z=2;X_-PEUC=CNC2eh_?#X&xvMd4!YkbLZZvIKhe(WV2j~Ib=~#YKaWuCOuV&y@ErO zsGOW<%sXdMS6Y;Z#DCm``ftJHL9s(nJ_QJqbBAqD19?m! z(Z`$##nbkLs+KGTM?$T0*w`S|;o08I-DI*HN>aTZUX0>WeBAn$y1_`j)Vzfi$wXPn zvw#N`X^>aay?31vqWmc$DLxcyNq;QMMHI{p!D=57)14IC&+IT-FJJ%jA$u5sROS%` zeYY9Ca)H}4T|L!mj9JlKKQ{NZ_cMSgpB1f%z`Lllgf4{l1JPgCY&ICa>GH}5E{GRT z8Kji=2RM*#K&yA_y6f+3BLcSyi$x;y?zJVrr>j%d%bxK)RSo1~SC`f>=iL|s*ipj0 zdsF1e_*^vt_~M^^0-8KHV6=RKX#{AcN@e)g0;1q&&rp}E5pZ*;H@VWDt91-#`N;WD zLb$i!x}}uXTSwpy%8^yj@@8~ill4oMDA1R7#impj>W@KQUD-OLS!Hq-#Z-t)7xZ_6ip|Jd&6+4t1f>l&@Uyg=3 zA3jM3WZpF669C9i#8{5NB&btg;^e+M5-M{zZ|PElqePlZrh{j`T-rp3Gq0#oOkw zA1~M7!miJzFa=DCsAYyG0ucui$vxl&DNA9aq`v`IG495%>Ix##lE!VGxHOwxx7~-J z?S^9tpT8S5IxPss3R&KdUv54NXI^jcz%SZMM9y9yTvS4Rq&eII3ORgrj10_0UIBWFf>!;p zJn%}tdHvY&;vIlpAxesV;e@Z*H%Tld`pPy+rP8p{B>UF^zFM;+Dt+mUOusVSzs_>3 z|5KLxPY3v4cx2L-4(;pUy0UsfdTuyBfdAws!6O+126IVBB$@ngbcUUit+o_~?^~XK z!QF_WOVW!K&eeq!cbPtBI&R$EKL3IJ=FHaIM<5qt%%|S}W?G0aAvcRU77s%FASlCW z|C65nzO`3|iXo9)0uvIXoG_Ulg8^YSq!0W((eHBR15d8Po%g28LO&2*d*pR%AF*_^ z`z5uI3&jv~9Hjd9dRuZIkwDz^D@0-k7d%y#7?GVt{j5f*v*MWWuV(F%6-AzOk%@`u zD8bBQ6h#fju8j1@%JN0jJP?%CGbOnP=hD(F zP)v+9COl1yH5NQhj53T^?VyXk?rq$YhZ{`x7ofimjGHYdQR?f!I{sD|#`JF-nCyRs znX;xTlIqV7SX5Ggc&}2MT7{aBAi-dV3SUKT5@Ih32!9^zm^qr1$^6)$dMM-XZXwRKah-H;&sf~{80}`atlGDf93(ZW85Kgw}F;POxwG3g;QPgP; zpiCPZG~iCeU0eBe8`mwvrJIM(ZGfJN=42K@M1fx3+{%&~C^#7>5iI9ZdP?Xj`J zUG_loF=XN`41G9)5s<)BEw0w1`DC41%LNxcUeris^pyriX(Xnqqd{aCYl(9dAbz+Y zl;6`A?^;D!NerC~x@#@k@#85KKw_uZr7_dbU(EKI5pLd;OPqv9(?=?LW{BudM@&&v zQ-CT|I}U9IJE0&;76Ee_8>K*xC^`DpO>Hritt^bWa(;JSr;PBUsPkTXSPU)*evkcB zCtTDMX}{|*weXczl_;?&^|6M_l~Flv_ss;Eos=u=Gji}1ZH1gv*h=Kqiy@$nE=;u>>cu6H-W2;AC12*a)WbB90SZY zdJ8(Y!KM?@B_MkN^P;M=`)-XD{T@lUffm^_9NW7IbsyC!qV>x)GcD>pV4y^2UkfU^ z?J2I;_4Dlk315T0?-2pcCpNcBDi@cVEgCJ@&VOGy^8gsyEwTFck^Yx=(>}*SMBFe8 z$$Efz^_dp=rSz@jFA|%igwH`qp4}?oONt`gt|*8a6$|>KAPWD+*E|p#!*tt2uefCk zTKI@e`~|fk-cbZJVwrqMLb>6mM)YAR#z@COww<4bD2_ZL%wf+Sh$$KIPtZB9(<^3G zK<0H%EJv7oF$?DXfhXi?Ns`t2eTsly1NH=7Z@OnNSMtC^BF6Sd6c4Q^PBrbL)(@1q zCs-Vx7`;wUy&tECZbSut66e|<5$L@)M0fIQwpotTE_$mAJ%R#2Uvc%WJ64~0TwcgL zy#usy^vh-%ej%miL7F^g6F$0E)`G!_=Ltx^ECQ(o1_p>uS?iQ|!Z>S~WL;g#lWx^0 z#w}6#YyauMAsOM%PB=ER^;~B z8bZ-WK*C*TH$9rX@cOcIo!*|Q+4%--Aj0n#Yqyz5Q{S(~_z=0uWbHkHyjFR7CbB+{ zBtt@YvBW;Xq6^7t+P?dQIpai1#d=K4suFGhir?QVD;S|Z<8bkmY!{JPNXnHUcUh(0 zcJobNZ#riP?HpFK`7jDT(xzwJmnVm}Q6nGuT%7=bI9;v|C6EvV|U@{s!9bN)-}b-=A!pIOa*_4o-()V5^w;w z+;TiOP&_f$FS#!~)^MRvnLfQe_v!NzUpJ&!w-@LCk++jW4U=LYBu5B6FnQP?2xz_D zeEf-L?WUrUgSw`MUA-F|aE=v22n6$0M8Hd>;p8rG+)%uj=x;Y&jvtI^q<5%pyOXCOH|G{+-5w?d%Z4k!(#6Uf_8m$%vcFq zLcT!MF(NzS2UEPz;R#MUw|bO!I5t-__}(Tf3EAuV+fy>+Ez<=IDQ!{=T zYx|pjx7g^BW&$e)vt*SdBWh>v1zmUO34Z(YuFRRnQA7p1MI<2IiA8H5v-W_@l5*iH z1)tDtq1n1Uta0>ED%%;Aa?R*roLrCpFeD%VME~CQ7`CJuNS3n75i|ji*RVn$dq~(3 zy{~}|hg!|zlP<5A;3acI5$fk9L)Vk+s@R$0K#lkg!i;#i<^RY3@jKIvZ(yQ4kTO#+ z2Zku&-MZTF@f^SeuV;_GmunhGBSK}T?)}T@@PKe}#_aq(pyIpN$YoGBuGyNf8~b?t zH27t%rzh&1vAYeb_r#oz$*K2izvsq}>PE3ZrYMtie#$8VsXKR9f*?5TR-_R@E(6ws zGx{2!N!(r}F5y}TXs^-}1609;bO{{C3wXySC6mc0_vkm6nMTv<27Nh+C1}*x}82u+j za{MPYi;}Emk@(?9J{_s6w4gwdL2wZe%qg)#Uj)2JB%~HhWGze0!Ja zjuj%F8-(i(VVK^|Dq00!Hu{53PP^XUjJ zprTwF-gMU1Tux=g3QoVP(#U9?0N@eD=C^X@bMg~;;O=cHrU{Dx6osZbKghFplt-Bu z{7iX>*1^Ye3db`jb5cZ-w~mPzt62dcT}h71Pei}8NK$68v}2Y?M;a1@VFJ?3$|Uwl zNZKNW+TQjOj>GdyZ6*vU;`Yl#d78Ad;;rTm?$VZ$?1S~HIW}y>yBidqN%H9`Z=U<- zCG^MZ;85R={$fcg@J?-ebG^U3o#hMud|yvoo)tW&D+~Re4D;g*%?R%;dl=F8*p3IV zeXL@MUPmjPy!_p|kuH*Cpcj6EX&*>LVA!&GHrmuj|K6JC5ypFcKvMS;xckoE(BA?n z6~e#WbxAkcZfYh-gcr_`g_-#ic*QY9NpVIlEkdNZ)q-Wrgzu<~$R?;$e0lDi)Zy7% z>hk?~H+=>IX!`k+%f^v2nr%jQz~G3g#dYt+IepkmYsY+{73z-mF9cv>YLX^=RdIb^ z;?#egr6m4+1PBhi!^nqh-3=?Y3*R=#!fshP$Y~=4M_wb45x)JG61oR;=?S8 z`ePiuZ_bvnNuLsNuX~y^YwJ>sZI!0d<2+3J9>cLk%1)H3$ll2K9(%$4>eA7(<>`|1 ze)pR5&EZK!IMQzGfg-p~U*o*LGz~7u(8}XzIQRy-!U7YtMTIe|DgQFmc%cHy_9^{o z`e88Oa_L>ckU6$O4*U**o7(!R`FzqkU8k4)xtJDw>!V#8H=9TbrNDi%;nH}c?p-~A z8Dr^b=|#GziKXIg6_TI4)p8FW90FVWWEp-$ADhAhyi38nPF@pv8{4sI-2DMrd!n*B zHJf_oyJFlJA_{>BrVbbwp8jQdH%i}hA$W*($oa45sx$ay(FnN7kYah}tZ@0?+#6*F zoa~13`?hVi6`ndno`5(1&BlOPIzRrfk5@pGx3G6@uB(F19323OA{vP#pMCxoUjcx# zP%qTQlSw!!Y_n3Q!U3~WjnOg{LNP?vMVyUzUkcUx+z^!P;;=tURD5iZ8o}Bc@g6X zFx7uYxYZ0>=f0f6S^8tVW{+CVCY!ol)5BgfUkWjj^Vx?eZOYv$#)keR3)&*uJYG)T zQWlHBu8o@}M=veby-JSpyET9BH;z1%40gj)Dy>m>vBlRc!3litQFklKKRK9ua;#mO z@IJ&X4qhvU$HyiJs65XP^tm2WsHlZYP{%RvVx!ggq33GF&Mt$I(Z&Or9h&oObZQSw zP}Ft94`0ijPzyq|3bikyUJJwy$>(LpHN2$(baZUc&@VS>GuX6F%LW4&`v|EX1p1Hk z2!c+Y#qxQ8YTSohi50GnA_{=kfufs8%X^{8F9NlHVFRjikFtNVFC!zRn7hP~w!RG=@ZK0rX7pm3ugvjmj4E^30X>A%q8Mo?8cAL2Un1QgODqz0kz1R~^u6cWM9M@v z;R^BaSIvxI6Hak!mL-&Rr&_RLd@EDYn;Afb?vsYq^)irJ9J=t*4=K zz`{02yJDAfx)PrGA@~Hg{*NKZ#m|?Wt*^BD?Qi{QmHz#pBB<|Z{AJl{Y~yI|WbR_D z`1N|x#`KE<+v$I4IRD?R28v%SnE&U8NsCjFRZ+8FxQd*-MT?Sr-9eU`yEUVjuVzDIFJvH zo98HyaX0EoiR`-IXuocDyEjFL6D_Kh<5YqewhcCD+u}~nNr_B}jF26 z3$if~T5va0w(Z!F`JM+WCxZU~Z=x2_lQizWtHLe#qFafeAK1HW4JovTIQn? zCwpS;ncm?#QM@LqrQ4{S1bs}vv>d2LDh-;7ZJ+EcPKO$+dqj%+qAFdqQSP5fzN2}X znw@zwnS)bu;PXwr*o$KJYkFpMomR46-vw(NRv4@PzQ52iZQ=-kYuhD)S|B!i+-0e9a*s{(@YJk?p>5TjKuO=m%RhWQjWfkDFL z%Gr**#cW&e-P*(O>472KA;L*Y+eQum93SXfm)+Cs3>gg@%N@jPuL9gq(ac_ zccQcRfAGHIJ`MHob+weYH#j-gBJp~#Idwg_UcYZ0cBRz#dRzm4v%GB!VDPU>-a=iO z*T~n6finwiN5`#ia?)to4@*SYv4Vj%GpXOAd&o+^JaL(dDrPpi66**yej&`NK01RG z0LqX6Q1BtdCbKS|t_QD?+DX4=;=Nx^0YQ1O`7`%mjEd%VMIb5$nu6R6l9u$r^9Aj1 zG}b8*7Ss2$KwFeWUV$q$UoU_)xeYTb+`0_do7?D@%$Zu)43p3^Hx#qJyeFFc83Gp2 zK%2f~%}i%5lG{5U@MOg(-fafQx0KxCq7_X(>s0V&#{IG63;|%#6!*plnNDKEoC6=1 zr>^@sLEa@{Tuw(R1_-zVO_q6XS!!+qzBm9^`6Ynj9LMKwt&K|gWw>uZwYyw|h^*FI zm4pb{zo|i82ajO0Bu*9ZlPx01)d#5 z9a%a-@|wk?F__Z=@~XNfTD9}ttt5a-i_#vQ232joq+`W$I*}>gA|`+mgyl^GqOD8w zk<@7>nXdY0E0@|_YCdtfuGQiaW!93#{5O?{ zgHaQ$0=@l6@|+)GC~yAp*DMn_vtrLM!lmtP-Yj@^sF$q7M0;A^*mn>TOd zUAvNl5uAv`1n@#IC8;D3{jnnwAxG3yB)25PjfB1XZ5q~d(`dk^nWhWc0&Yb?H#s-dux47iN^A~=)p6ypZZMLs zwlo!sUn#@S`)4CTsX46?^fU^`F_@R{08A0Xnwza`4fUl${? znphCWnPTbE{4It5Jc~Kp0GUmmr|`^AeT$WyGY&OxtU1=w#fLi(eobV&X_LWj ztwJZDTDX?3lR>W_z6HAvUf0~At4hcgsq*2jzK7f?@dF`(p-hJfg%b->3hrCRfSdNO z&deMbQE9MEc_t_# z;&*c6MkUb_Sf+rXgT-knTljQ@H(W!=ZRA#utC4ge6njYOiHq7vt>;*CT2#la2geGK z`|{gtLIJ0b50KRJG`Dn2`kii&?c;$Lto9=(4Rp>tUDKPbj`DAXVFi($>n7>#UF=2d zu&Q(Ad$UR$;n@Q~rl_8QvZUGlX6r;s^R-yLKtj*v{8ePURGqZklwV(pudjgFgZd(k zps_J=Ph@A7u@&AFRl#-xV3-W1?uA}yXpn6>LfSxhhK&X-5W^B}fVgg$esQo|&`=Gz zq8d%`(jJapqz5(LDilFz@J@|HC-?EocmcdCG-;1`F(O4?)^a&68zB3M@x4ZQ_q3OK zxpUL9?h3zVXk9hdMLP7@S*h~@yN+r(Qg4W8`9WwUL}s@<`}b-`YvCPHHO@#e+&+R6HFz{&Gv3*dcmrC5F`~~=A)MhebBvct;_&+B@K@5j zR|Q+!$CfR8K0t@g{_^Zx=HU-VoYs!kA0&1)d?WNin4~v;y`pB@IyyX4;K ze>H)U(nTi>Uf@HnKtP7pOUM~?p+1%Sd*#=%8a%*6E#;ks+e_i(9M&MfwM@SHj=#Qt z!<}b6BJQP&QxvHQ(f5M>h#02hfw-OWM9T??Dbx2t34i-Xw^hWGoJHoVhL!%>75e{c z9V>0_==eo4|Cz|Y#?1dIi&rK6gJ_O?E+i+@XwpEIl7&OALe=jve-}pRL!*qZF89ce zt>BHL;wwvIJ**Xm*72K4&Ezl$EmJx!@o5;*6B_MF*UH=0b|RZE7aikZ9@%R5-(>ul zmxw!C%KNRx1Tked$fXyY)v@1|xxI1cugC@^WK0Uw+99XKA>wp^qrZgEU-Puc3GYJD?k~%=3B9IqFrzliXisoS#i0yZLo-#VI zy-G#>CLT))HY!+GQ%+3^;I zxWU3H4F7}JLi(3qr+*P!@xSft{4a>@e?Y-i-@*955!)u^FaH?+pWF+}D9K4EAcM4g zl>(B+c~9cmzl*)CgY(7qJd)TxfEEC3xjXhKX$u795jMU39HpB?Pt^k0-(e4ePslk^~^hu*&n^7iSC z!f2@wnM+94o+@%-rudT|EtzVBR=c_Ii!Mc3*%CFNeXyy^o_1ND68q~yy|bck-E z7VSdAnaDotDnXS3la^~tvUB-o51Whl0G0y%C0ie z1bke%qKD(`*oZH1BtoIgWBOCZn)s^x{L`SA)|=)jRAOGW`ash4qp&@O z>ew88$OWDm9{Y+?s~2FAP>W!dcSf7e{y};M&T$2ta<5zFy%DwT+o>ei%gl5GJ#y$; zC(&&yPTS=f%>FEtBbuu@4oL~)6XaG|&WXnAW~B^4ntY~=0S%$ofB2Gi%yI{pe?g?= zZy_T5@7I3+gvftwOcW{opYdE}q60PFFHmF)O&aa+P>Hw*<%D!FDGRatOF5bG_^%P& z*51xd$ju%UnmF{#2W~+(+OZWY9yR1pNCTs(i^=q)Yd5>DulENKUX&>Y5CD0C<}{xo zoKvADl-vC5+FHI!LX$QbhTBq^qJMK5v)GH;N^~6wQ+cIUs#!INT5Dn%p5Xo}oI5Wi zNPV8Q*~NHnX;ud9rjmJu?7ZXy@P~MSY13GME^d_FelnveEWiD;Iqy$5{lOI)tUmQ;4vZ1F#@vSeyusf5>6tr2)eEVkz7Tz>zF({b zHA?`#7AZh-z6!JTy<3RE7t)cx9UX=cfT{{q^lLp>og;`OQh!sf#UbJ5?Dyy!qbW%n z`mpup9GwW-TLS(e1CppSa-a65p@$N5LT&nJ&T-;cj%f8)rwmuhh>K(zzELMO_!aPg z!Z{8pdL$*99=(gSDsF6VgxpQ#b60Mi4{;z9$hFhM<(6y$~z zl#U};hRiF_OO)DOUTp1o)$D`m)UZHqGZrC^XOuQKo#?kOEYNQYa<4&^LhJDRDRm*j z)_QmM1Fj)bAyyT$=K~*P(Qu*zcKehn%y{DfzaLi}058bm+9kC zGQGn1T0&tBMqU#SO2aV}Cm-o(XdWHaFoR{8x6NFA<*&O1{khwDlAg&S;*`Gf{pfL~ zd9-4p!49jS{#VGb8km<7PF76#3-+L)tY?6*tV!*lL*gYp*AS%TphMCj-2`*w2iRZ3 z14*D{)TuB0`2Q__ME?-S$54wVIdNtOFpjDD!=lN zS2pxkSv9z=XvBwO%q)2%U>Wf>-RAn@Z?bGt94NDxAv`m_iK&s9vdH5zAybbCv# z52^7Zzw(N0Xj;y>>7hwl9a6~l1L~s*T^OGl!l6BV14Pft_Un{y_0IRZSQjYBhBsQ5e@RUMs5G84*43&_{b2tPwvRx^;8lZscl75q1%> z0SMWUHbHZ?f87Jf+@$%$FLhbb->S?07h}|a#?gPadH-XKs`yWXIz^4AL(o;f{0se;mi;c|C@#l-9VIw>lWR^l@rn4vD3V9A#p%K7sWZdCBaZo^ zfKvrqEn0?%(D-Q7Ki;9lv&bOw(-fVFC;CL;ATrxwLybLu|5I7Qu-=Q2?3Oq0l)X&hSXlr)rl$|Gsqpws@b#DAy23bt#hMQ=q0I)Do;%elJBX z%L7K>uyq!PtV~{!Tnd;Gjo65==X^3>0M8~)51ouccRy$QQHVD81%Fcx8?F{je}e&< z^cb90f^@=j6YQMw!$fbQBw8caKsLBMA3oAFn=}wq6_5wbyh*6^DGO1;RvHvC^*a5z z@e|TwZH=N-`Pep?-X`;%V@Kt=cn@q!JCniGC6>|DHFig)G(7p}?njQN)JquFcfm+0 zCv&u6aCpsf=%HkaM1u@mCi1)Bf+XARH-MIYWnjZK{nz54il91eEq%J3KBXUraAdS%a$a{)!&r6BiHyJ$k;voGEd|0euZhtjxJCsH&v!FRvOs6 z(q)m-|0EnWwMS|}oL}@2M)58r=>9CexpwiI-iP&lNOeMe%=@RF2c-~g!R0I1nS5z_ z{&j`T@`)u0wqAl28cT!f{q*j?x6o>?-w)TPye<%zW4pm{RJd93l&>Z!en zVPld&PW3Fs_9?9%3QPGOlTAi@I0G^{b`b=L#K;oJ?Qxz&HG9o;fv*~^KcJJOdNelY zJ7c#N-jA)mylX&y8=fxT``?$^XX}tI>u`;?bZQL#;4KLrxr+PuedR zOoA2c<(r6hWXn!K;J|JD<q9$W#*FSIuJsyH z!FMvDoT~fLw@dftIQjDyNd+A3CT+?}RnD^wDZDaxVhq>=mJv!1uN1ZdTtO$aXj5fK zW235&zn)FRae zkVk`LK6#SJhQOBWN(r(dKr|m9NTeN1vIEWwzB2z5@PN>NSXK4;9Ufb=P4p{pP95VWVL>rkAqV816C zUaNfmhO{N!SQA|J@abMw?nA! zz{BhtFiMc=;bCxFUrO~!R>qx4_O0jJKiGcun_+}PZU?Qxib_I0>gmRH1lEpA$VuT& zQ(j{XC0P#Yt3m7&$x!`O60Rp{@AEDym!!yF63LhCd{QoSQNT^Ea4pHtFQcIpBu8ok z=G;wEK#(TU{d5;RWj_@}hZ&7WwK3{*DPhmGB-*Pt7H-oleAIUXq-1ON1c2(P$(zb< zw4w=#Xs8q?Xc_+3Rv>IKc$4`m0TyR}|Bb$j)6fEGb8n9IJaXzH!f>=a&F7hwamjga ziew1|`^y7ia#AhHs=%qx7As|lhN@zx#YFm7ZQ)aHlqK>OHA=~ieU%c%8TXC4wf={r z!*tdn58kwCtPstp2<%1s@5kWjh7I;bL`!1~>$^YmjhyK=G3>05e7K^W|I0kTkWSR!aYoJO}Cj0F{DA;AM66@IMkLcxeosER^AvJb z$N|ga%`8nC$Vq@y$Yc%5E0>mzEgS7E(XuO>r7G{%tM#Rz_Z&`FoiRMkaXg`Egh_ry>#iev(h&cK0OA|6nwTH<^XU~gt(>Jey8JJ$0lg%eqYIqf( z`&G~9K$yUNQ~pm9J{fD+44N78QVH}1kR)tTN})IzTJz#f}-S-!VbI+VJU0-+g?b|(dtG?n$avMzxgCpaV zZS$Mm6o$|?e$D+x7+)z}O7oPB+q!pCpX zY*~s9D;UXushRjCuw^%N8*{d-pgiv>`;&YwU7U@zb!NyYj^>A|dKv!HljIsm?;iVw z>X@kFp)=ux?lJ2oo~gYx@TgQW_wbR9QZB^P%*=vQwWk#~cxOtf*NxyjWBN{d>2DMoADJfmE>W4xr$hwrc z<{Rc^6TE7^P7*VZeexuji`%7KNQ6$-rE{<97zYb7{3toN__(H9lpOLQ*og%M-Sm6H zM`yl|)vdjf6*85Q=qU()Jo!8nE>TmB-?WRA6eH5VLV5B;H4`UFurLCRpuIRYrpC5l1Yu$0EaWrx%}E~}@@zN5hy{cQy&$wJi^oqN z6|k_DRi`YJ4M-yZT8pWj04R=Wq)z=jXwhsekXp4u>2V3~)t}mI(=H!sbM2@Qjns$2 z82gXS@^bBTyxe-)%1fu;fI~%@pT^1MV=>Z{xmZ{WVs=hx4GMJ04RY-i`)C%B`7P$? zt*BL(%wz5cs&DgY@pRjKeVD3g!lVpR34Bh-ux8#^WjxYdg*6d-sUwmPcktAa$448! zkzvpTp#G&lNk4FNOd&1!3SZglaNV~FFJc`?j-NNEN9f!FtCHQj&r)#)3*lqTUhKTU zptMt@uG&cyCP!++fMH;J!RC>M$U8jj z$IIuHjAg%oRsEK>J8!RuI(k(`uAT<1gAb2kUc^anBm->b(?KN?hj)PmnL%?nIQ($$ zbH;JkJRcQ>!2rj`qWS?QJd@V}nzVZs>j7Lk@^9KM^qx0dn6xW#yFKqJ1R_2Dk?bA* zJZ~&*ys&@0i_3mNe)&5J-uuFo&yS(8eVuKJ5sx0@iN!J(kH8f2C{=ppFTRfy^Qfq7 zX9tuWoNqZ&;72U(M8Vh(cQRQY8wZs|3(7f=Q*|I>7Gxfbu(7(2PGkDe@;F$@+2Wg3 zSg38BAXkc54h4j8Y?BO%d^LL*LVxHvID^+f^47kBEHS!PbpO1HyUx~{&@Mj-DRSD(&2{OPkC(uB$FqFsEvnY!s8JiUL53 zW#J6^RZ25e+YjCFHU1v)6!iOWflV|^TH55FjIf7`>9-Sd%#USU&m>b7GIQ4yvLRMx z&5oFv@!wF7u)RTdm?O4fBu=SE?S&ehG`3p6Q%~7F4E`XT@FsY!W05rbff+LmS^4LN z^^h@*l30m4dbEO1&O>E!8%ImXUsmxt7QVgGNGmQH!4%usI7SDMX|Nr42nrIm^OC7)M=~Z;lP$iJSs} zdsva%EV+QEntmiD-{Fe!tyaU(2_M(Vt4I54!aR}dZnu#K7(Q;~q_~nuJOWE*S8&lN zSSR7(16OzAdMG;;3$?DFp6hs-PvlLmYvLK!|M|!n-?v=i<0!UZoz_6HOkN;sxeOVn z&8czTqz?7e_-gfqM4RWhb~Z~Asoy%2^jwt`j*s}9fw-R6OX8^l`_b*xEHcijwDOPy zidk487k7dcQHnR@jlHtc7NPI5+x8+(*H)qlXEG@jheE&Yg%a!5cJBp9Wfj-F3yVW# zoS~j%>J5X-UprmK0#}0j5kfFPEzetTrJ$-Qt2VdXTIdlalYr=4xDm=vh)MNrUlCQR zygOaQds50Ww$p%aT53EKYnjDtVbv_$B_ej4SwpkW)|G4j_*>{R$UdzU#1@%Kb_eh* zPvXF!_LWiV#GE~F z%KLs|&>ldGZgFbPdt&&|n{C^aQ5qkS)x=CR1&MtVQlAC!NW>%gbCfoU;u$Gw($?q3 zZOtTL71_E>TWy~1;8MotW&k^|RbK-et+TvJ88tg)VhJa2rg0p=E@)DKL7~x&Gj&pN zD7Cni`uSNKoh)bg;pjx`4?HHD6)KD74*MQC>z-W`suCHFA>{s5YX%(tC*`9yOdcb^ zcqojkRkT6({;E!oLmc?Xrvew>I*ysLu|jz4LlvJX+ACd!^(KHX?Ru}Q1(2MHNKKjs zC3ZKVA-Y#&5O%NLYf?o`B2s3FtbxY36t z*f6gQEW};b=>mgzd4Ttx6hI&ozi{5tMQ$lZiyo`2=o1XQlvQ(Q7o^DAtzOq ze{enA1A}cPb?qj8x5ss`@_2rsuAkBvoXVL-qp#2n4@#!XJ>*!PxsPI`hBo&*od%h6 z4c+*rZRa|iZp&+4O2R`Og5L(N(qT zx2b~PRdn#-KCG(xqqPxO;ZC7(Pn9>POY7))C9Aq%Ds={XK!1tt)z+RyZnLlo)I8N9JKJ*sLiG~2E1bTV z-pQ6#+(Q7OrQw_Q6>x@mt{-jWxu$)&fRH&wT^?K;B048oWj%HDWn0ax0-UYmCHIr@ z#m~>gZskTO?mgk;0p?*&t2tj*D3@IMcvTIVJSkR&Dv9GdTAauUs*ive&nlYhiUyMm zfm8vwBL#>Bx%vAM zE7gvntWfhRKdQrbDcAa44N`>oDNMe8R*;R@?YXve$Ono*;Uu0Hp6c!5MI#d z`*6tv*@AsSzJr-0D2Jw#I0vrEKl`&mO{FX-ejqMfHFEB4vC5>{)5qOpKQ_ymm&aY} zLOsz2HwyNd88)W=#svNj;O2R zS=2llz+lu0Ob4?(09sazN=eLexlg&Wx{hF-eDOqkWlzF8wt_;cl1+_x=h*LD_U@yr z&!#O?%F2feKI-nzeX?6GEiy29`jlg3;FOA$e6oC)=#U}CHQS)zWwr_@`L1_^)%dF- zZDrBM_`?mV7oPBy5zT#ctjLMl85S{SE5y-mfvkpsY$xsS9tDc$I9>>HDT)~7FU%sh zw$@c!vWjVBk6EC_OW;7Z%%E?ylOmhSD-=8O&s{R`XE-7^;KCM_b3C`Xo z$QFVvKA#bXXIIG@$&vhS#m6%egz9HQRS{(=i}W7RsO3$rE@Ko=)#t`IXe*z*rnT2L zGB!ka%fgCFS&dF!M#l*Xp|dpwF-dz~d=5kh=oHzJ^%mP}V#iOBG&F6H#?OpcaPlbh z`jEzRFZHw1CWMbF;OxGuQ2Vg4J69fO2xFLyO0$HYr@7%w5gkZW4hn2ri}#T|026$3Xibk>)ua(>-BaKW$*mA zxF@#Bv-5I9FtAF>pS`E~rBCEHM~KlM>DAXvcfb2YidD?7xq?04qxW|Eehg=#gca3m zDUYP9j?}}csrL2F#|X~XMj1AWgmw!oLduHrt*DZo*|JQab<|yd$VWP$m>!$gTf--N zMv!E4f@S{og(<1zI0r1NE~^XY@$7NqDzDhFZrbIt4cL?U2&4xOPU*N4#zWjqhMqI5 z_lgo-#1>tK{&=4x8j=tpzso zqg)o+QZ{)*#s$o3Pd*#?qkdQ^;5PhA_Q#$Np6g~X(O3#22?zK~PZA?a{pc4dRZh1? z+kyR1`Ftm9O}GmhX10(hG#6&arj%Gjes)!3d$1II2*w$1w!(tVVCFP9^jUDNWsRn< ze;D0li0}hmi0!bC=4&Df=~J-|UFA?*C?D87WL!6W>7Hji^JxlBsMmgMzGd1CWg?lL z^({j*)fWl(oG0fgBi2WK?=}~bR>~(CBt391 z;UK|Jj3v?Jp?jcZA%%{rvxH%H?lGch)5iD(Acv8%mH-*a-r!H{!N|Y}qaO}e631ELqnk=-u%?`6c}tgK|FSn)sNJ@ z`3PpiYFu}^nSzjchfySL@V{nzNcLosI%zm7;dPGl$~siA0;b6{U~>!emyCZrV&SJM z=cjT1@-5)9Na}zE+hnh(Mf@vprvT2V%U!3fW*;w@$q)^9E>^jrBX6_2GXrV$xqc1= zTl_ooSB5HlvfS&+Nk=EUCzA74k30#vS3`;*n-!T)6WFvm;gVIq^hjg(iZ)FLa$m^9 zkT!EXm3$D4e}9H>pu_wE2Yn)HPLwxU8GrSM z$CTN}fxQqI&;C_~3-ia#v8OP@8ib9s)>P{K)LgF95BF25+pdIKnn(6tG!o+QvvWtA zQUvyE7;_tjCYP(bu;Xqg=#|AJ!5v)S3O9Jr*`Y7czB1`Qp)csyxrk0+sQfWgg18v02MU(q5O8O!S5x+ zRf~823`hIiLukQ#91i)o5`a&9$ofBqnfoL6w{zFY?*g zUXY*-J@7gU!VP6^KmI~))%9W0n|IPLYps*gc@ftXk6=rr8a;&@QpQP^Oec<(Q?ohqeWbqz!f1(*w&>@bMPDEk`@MZ zf6+JKX&v&#od0h7nl{YNCRnT3-mSi8*<4FOi``*DH0WIxhrm&9qalSusT92so0h~` z9%_Qs3;YBW9<=!yy1HHH)YJrF=J8dCS`{*e0{HNlXgjE^5negJp-$mcc|darMuC#2 zY@L^17Gm=U$J3WN{l#cb_{kE{(FuI~9FE1r)v`Vl1@KMufWUU8zwf` zRI?^*$M(@0H%0bK6S$@EO;Ddb*1ODNGk+1y)jN?bU3faQM+1cjWb<5fqjg>1C|ESs zC}`a#Y+gotS;(QOJc!;bva%LkPqFU)?#oDyg~q!m&Hdsn$LMH6)vI(5?F)kI7YuitbOF>FOjPm zCufTkuh5EU_Lz#si-S9H8kNvA!U$j#Us&&p3aM#)8mz*YwFo{C{h+dF!udYgph17r zNm1aHzH1tRZQs0!$jp}+46q%Xwa zP;$i46ccl2scqcrSZ7OoXh5;-!E|kiXaH0zKuF(HvV}?*A{lW&gKHgql3fL$tFWE1 zzpt$}>m^qxmR8*9XO>V3cX84X$Xb6gs1W$5ikfs{Z_-bLqhISQs|D3e5)ZhL7hQ{u zC*9i+0Tm`126J*z{-RKR|`qR;4+GxkNX=?K4Z;|oymgu!1k0r9n+-=GFh0rVyT2VjLsasA4z%K}XFpg_y z(RMdEh2YL6!(3VGy!bH*qs)(V4a&kiXyhd3{M76Kstr6+~M_t4d%%= zqln?B-{wO}USay1;bo<4j1SVU{HT51i?7qx)|=gA_>C7@mazgQg|~I~{itdvuAw*J z#1}&*#s8wKcBjo|_I!2+n|9>w#cs!7mAjr`ViD*#Ex~Y`O2)piwKV{g1dv?e6K+=KO{@^D z@Nmhi`r@{6Q(i{EJm5=Nte)+ln_fBU{^Wj+aJ_uyd5MH3K=0@He;PaS{Flqe-p0b% z(D9#;!RACs%MuG9`hP`!Abc*U?X;-h=nX5ya~4HuB5OqU(bdM^-i(EX*Bl%ENnvAE-W8K)0Tyv<-7tpmj9Fc=bNC4qiV^`4>{hR?pB`<7U> z2{pWJ=G2v1WJJ z=&Qm@dXj(~ICOc!BC^Y`S*2a48b2V&m1cTSK86i4*9`=_u|x{B8lPPSaFbgB&-IhE zIz$TsOO*?2cH7lzy#qaJEGt9L7m?XvMv1mA1hmSLnCCrVHD62cysXn_Bi}Nu4M>eQ z*JusbR!9hA@kN#{?k^q=$0{Ac`INpZ1)J9?-Mr4qwLrR`;vZ86BdTSC+@sAljDHpV z2?5X35@^Cq{6z2AduhqqrqmZMu~q5gou_sY^D6uuvG^FS}`~+|dSP+8iFhpY^4&Sfv&L+JGb_u}FTXe73|$Ma4rQ5O66H3jWgooEFisi>7Ga4F`k;8+ zY|2eqbarmPyRK}N9SnnWD0*Cz2=f#%YWFBKKFUk&BbPVV>p8E$Cpbiq_$s9WYBJ@% z$z^efO)G&Yvc0rLddaqfhKoEbGCYc08wrM@TW^mU-g4EP$B%5i9&tS8cq=2!xFQ-N zx1C>h1eD5+7zTDX7CTaV_+Ef#7n+fr9gN3YuV~1QGk7)&EssM#dZ(e;6U;`d*>FHx zda3B|)wux==${v~-X6fMKZK@h%&fxw(aTex76MpF*Zlr2#uwR{d29#ediLKtZ+&fg+Y}Nh%1!#@_T#ox1+YjkQ4xQ@3RJj~4p2`i5r4 zKW&I~s~Lz<19UfCL;cb4-%gawUp2pDQ^?=%((Bp)!;|Nof}ovC4^(*kx}4gL0KpS} zrbf`l#__sNfUUH?IRKO{`QPdQzghGa04!mo@k5X$Y(eH!ywMK20>QaJtKv_?yy>T~ zv5CHum7_3-U%|>o-v~sBcmELy+9_S#1e_erK$O7dhX17ox!-|K4o^r~Q0z<}h^U0| zP6t{+^Yza=@cTjbzV_F4$H=81t&5<4syo_kKt&z?Ui*K>_19`Y1-;jL09w3=_@Gi) zchowA9twY}7Q~z4sBiV3ilO&A%PLc#e-2uI)}W~+yQ6iG}dAn#m z!yT0b#s*~yO!Z~_17B#f)jE7MDzXo z{Z1{|`@O$iHtp^*w&wo{#vd~PI%3?fk$4~AcBzu<>T5Rw1UC`>HNf3kY`^LLo1OBy z*vWrI5fm8ux2C@?eR98>w@WizbBo^y5Ip@?s(xMg@P!^!`m-#1|Y z}}>fftyi*u0ZfwgZ~Zo_d))_H+diK zHYe9Lo!^ZB!Rw9xHQZ0g{qE!5=2ud>0R>%w;0PA~8uuUZf8FoFZEA??^qd<3f;U+G z>mK}!)#*O)?F{_8Ciijt*T6sFp}3EGJFWTdfMZ?$HSX^>mB3$S``-t?oewH;wC_bc1p&d=b#F)Zi`1fTW$*TBED>g`ze>zt1p0fMs!{%f?KXMo(d@aKbI@F(B-UnAUhHD3P^z7Zh!RMx*m_}OlG+o^T!xV#Y{ zI3~z7@$&=OKX+r^pP}2%6tNpf&=m-dp8pHf`)B1_=bS$sJ)l6ZI>5l_L4UcRpWUd1 H*Ps3mB7Q<; literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100755 index 000000000..42fa4da21 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 000000000..961a82500 --- /dev/null +++ b/mvnw @@ -0,0 +1,286 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100755 index 000000000..830073a17 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,161 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" +FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% From 2877a656e0b17c4cebf5518e4cf3dd40edb485d3 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 2 May 2018 08:04:15 +0200 Subject: [PATCH 06/35] Avoids parsing namespace when using existing namespace --- .../java/com/opensymphony/xwork2/ActionChainResult.java | 9 +++------ .../java/org/apache/struts2/result/PostbackResult.java | 1 + .../struts2/result/ServletActionRedirectResult.java | 1 + .../org/apache/struts2/result/StrutsResultSupport.java | 4 +++- .../portlet/result/PortletActionRedirectResult.java | 1 + 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java b/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java index 1550eb98f..b84289746 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java +++ b/core/src/main/java/com/opensymphony/xwork2/ActionChainResult.java @@ -201,13 +201,10 @@ public class ActionChainResult implements Result { * @param invocation the DefaultActionInvocation calling the action call stack */ public void execute(ActionInvocation invocation) throws Exception { - // if the finalNamespace wasn't explicitly defined, assume the current one - if (this.namespace == null) { - this.namespace = invocation.getProxy().getNamespace(); - } - ValueStack stack = ActionContext.getContext().getValueStack(); - String finalNamespace = TextParseUtil.translateVariables(namespace, stack); + String finalNamespace = this.namespace != null + ? TextParseUtil.translateVariables(namespace, stack) + : invocation.getProxy().getNamespace(); String finalActionName = TextParseUtil.translateVariables(actionName, stack); String finalMethodName = this.methodName != null ? TextParseUtil.translateVariables(this.methodName, stack) diff --git a/core/src/main/java/org/apache/struts2/result/PostbackResult.java b/core/src/main/java/org/apache/struts2/result/PostbackResult.java index d040fbbb7..4c1e52af0 100644 --- a/core/src/main/java/org/apache/struts2/result/PostbackResult.java +++ b/core/src/main/java/org/apache/struts2/result/PostbackResult.java @@ -136,6 +136,7 @@ public class PostbackResult extends StrutsResultSupport { actionName = conditionalParse(actionName, invocation); if (namespace == null) { namespace = invocation.getProxy().getNamespace(); + parseLocation = false; } else { namespace = conditionalParse(namespace, invocation); } diff --git a/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java b/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java index b5356ad15..5b800e249 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java @@ -161,6 +161,7 @@ public class ServletActionRedirectResult extends ServletRedirectResult implement actionName = conditionalParse(actionName, invocation); if (namespace == null) { namespace = invocation.getProxy().getNamespace(); + parseLocation = false; } else { namespace = conditionalParse(namespace, invocation); } diff --git a/core/src/main/java/org/apache/struts2/result/StrutsResultSupport.java b/core/src/main/java/org/apache/struts2/result/StrutsResultSupport.java index a21e095d8..d5307d279 100644 --- a/core/src/main/java/org/apache/struts2/result/StrutsResultSupport.java +++ b/core/src/main/java/org/apache/struts2/result/StrutsResultSupport.java @@ -122,6 +122,8 @@ public abstract class StrutsResultSupport implements Result, StrutsStatics { /** use UTF-8 as this is the recommended encoding by W3C to avoid incompatibilities. */ public static final String DEFAULT_URL_ENCODING = "UTF-8"; + protected boolean parseLocation = true; + private boolean parse; private boolean encode; private String location; @@ -200,7 +202,7 @@ public abstract class StrutsResultSupport implements Result, StrutsStatics { * @throws Exception if an error occurs while executing the result. */ public void execute(ActionInvocation invocation) throws Exception { - lastFinalLocation = conditionalParse(location, invocation); + lastFinalLocation = parseLocation ? conditionalParse(location, invocation) : location; doExecute(lastFinalLocation, invocation); } diff --git a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java index 4b561cadd..db4f98a38 100644 --- a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java +++ b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java @@ -179,6 +179,7 @@ public class PortletActionRedirectResult extends PortletResult { } if (namespace == null) { namespace = invocation.getProxy().getNamespace(); + parseLocation = false; } else { namespace = conditionalParse(namespace, invocation); } From 8d86442ce787358f940996eabfb49c548a4b6d34 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 2 May 2018 08:25:06 +0200 Subject: [PATCH 07/35] Increases scope when location parsing is avoided --- .../main/java/org/apache/struts2/result/PostbackResult.java | 2 +- .../org/apache/struts2/result/ServletActionRedirectResult.java | 2 +- .../struts2/portlet/result/PortletActionRedirectResult.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/result/PostbackResult.java b/core/src/main/java/org/apache/struts2/result/PostbackResult.java index 4c1e52af0..1a275d52e 100644 --- a/core/src/main/java/org/apache/struts2/result/PostbackResult.java +++ b/core/src/main/java/org/apache/struts2/result/PostbackResult.java @@ -134,9 +134,9 @@ public class PostbackResult extends StrutsResultSupport { if (actionName != null) { actionName = conditionalParse(actionName, invocation); + parseLocation = false; if (namespace == null) { namespace = invocation.getProxy().getNamespace(); - parseLocation = false; } else { namespace = conditionalParse(namespace, invocation); } diff --git a/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java b/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java index 5b800e249..ed6825b12 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletActionRedirectResult.java @@ -159,9 +159,9 @@ public class ServletActionRedirectResult extends ServletRedirectResult implement */ public void execute(ActionInvocation invocation) throws Exception { actionName = conditionalParse(actionName, invocation); + parseLocation = false; if (namespace == null) { namespace = invocation.getProxy().getNamespace(); - parseLocation = false; } else { namespace = conditionalParse(namespace, invocation); } diff --git a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java index db4f98a38..282f65323 100644 --- a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java +++ b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java @@ -172,6 +172,8 @@ public class PortletActionRedirectResult extends PortletResult { */ public void execute(ActionInvocation invocation) throws Exception { actionName = conditionalParse(actionName, invocation); + parseLocation = false; + String portletNamespace = (String)invocation.getInvocationContext().get(PortletConstants.PORTLET_NAMESPACE); if (portletMode != null) { Map namespaceMap = getNamespaceMap(invocation); @@ -179,7 +181,6 @@ public class PortletActionRedirectResult extends PortletResult { } if (namespace == null) { namespace = invocation.getProxy().getNamespace(); - parseLocation = false; } else { namespace = conditionalParse(namespace, invocation); } From 45cb68c57c0b03009fe6ba9952730ac61850978e Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 2 May 2018 08:35:52 +0200 Subject: [PATCH 08/35] Upgrades Jackson libs to version 2.9.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dcd81071e..70077a13b 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ 3.0.8 1.0.7 2.10.0 - 2.9.4 + 2.9.5 1.6 From faf1ac96b51a8a538e2aa9917a49d1bf705bc0f0 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 29 May 2018 12:58:44 +0200 Subject: [PATCH 09/35] Makes OgnlUtil more immutable --- .../opensymphony/xwork2/ognl/OgnlUtil.java | 51 ++++++++++++++----- .../xwork2/ognl/OgnlUtilTest.java | 12 ++--- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java index 808efef9d..bb0d4e825 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -61,14 +61,36 @@ public class OgnlUtil { private boolean enableExpressionCache = true; private boolean enableEvalExpression; - private Set> excludedClasses = Collections.emptySet(); - private Set excludedPackageNamePatterns = Collections.emptySet(); - private Set excludedPackageNames = Collections.emptySet(); + private Set> excludedClasses; + private Set excludedPackageNamePatterns; + private Set excludedPackageNames; private Container container; private boolean allowStaticMethodAccess; private boolean disallowProxyMemberAccess; + public OgnlUtil( + @Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false) + String commaDelimitedClasses, + @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) + String commaDelimitedPackagePatterns, + @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false) + String commaDelimitedPackageNames + ) { + excludedClasses = Collections.unmodifiableSet(parseExcludedClasses(commaDelimitedClasses)); + excludedPackageNamePatterns = Collections.unmodifiableSet(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns)); + excludedPackageNames = Collections.unmodifiableSet(parseExcludedPackageNames(commaDelimitedPackageNames)); + } + + /** + * Constructor used by internal DI + */ + public OgnlUtil() { + excludedClasses = Collections.emptySet(); + excludedPackageNamePatterns = Collections.emptySet(); + excludedPackageNames = Collections.emptySet(); + } + @Inject public void setXWorkConverter(XWorkConverter conv) { this.defaultConverter = new OgnlTypeConverterWrapper(conv); @@ -93,8 +115,7 @@ public class OgnlUtil { } } - @Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false) - public void setExcludedClasses(String commaDelimitedClasses) { + private Set> parseExcludedClasses(String commaDelimitedClasses) { Set classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses); Set> classes = new HashSet<>(); @@ -106,11 +127,11 @@ public class OgnlUtil { } } - excludedClasses = Collections.unmodifiableSet(classes); + return classes; } - @Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) - public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) { + + private Set parseExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) { Set packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns); Set packageNamePatterns = new HashSet<>(); @@ -118,12 +139,11 @@ public class OgnlUtil { packageNamePatterns.add(Pattern.compile(pattern)); } - excludedPackageNamePatterns = Collections.unmodifiableSet(packageNamePatterns); + return packageNamePatterns; } - @Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false) - public void setExcludedPackageNames(String commaDelimitedPackageNames) { - excludedPackageNames = Collections.unmodifiableSet(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames)); + private Set parseExcludedPackageNames(String commaDelimitedPackageNames) { + return TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames); } public Set> getExcludedClasses() { @@ -679,6 +699,13 @@ public class OgnlUtil { return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter); } + protected void addExcludedClasses(String commaDelimitedClasses) { + Set> existingClasses = new HashSet<>(excludedClasses); + existingClasses.addAll(parseExcludedClasses(commaDelimitedClasses)); + + excludedClasses = Collections.unmodifiableSet(existingClasses); + } + private interface OgnlTask { T execute(Object tree) throws OgnlException; } diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java index 3b3c8ae01..02f0f706a 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java @@ -41,7 +41,7 @@ public class OgnlUtilTest extends XWorkTestCase { ognlUtil = container.getInstance(OgnlUtil.class); } - public void testCanSetADependentObject() throws Exception { + public void testCanSetADependentObject() { String dogName = "fido"; OgnlRuntime.setNullHandler(Owner.class, new NullHandler() { @@ -653,7 +653,7 @@ public class OgnlUtilTest extends XWorkTestCase { Exception expected = null; try { - ognlUtil.setExcludedClasses(Object.class.getName()); + ognlUtil.addExcludedClasses(Object.class.getName()); ognlUtil.setValue("class.classLoader.defaultAssertionStatus", ognlUtil.createDefaultContext(foo), foo, true); fail(); } catch (OgnlException e) { @@ -669,7 +669,7 @@ public class OgnlUtilTest extends XWorkTestCase { Exception expected = null; try { - ognlUtil.setExcludedClasses(Object.class.getName()); + ognlUtil.addExcludedClasses(Object.class.getName()); ognlUtil.setValue("Class.ClassLoader.DefaultAssertionStatus", ognlUtil.createDefaultContext(foo), foo, true); fail(); } catch (OgnlException e) { @@ -685,7 +685,7 @@ public class OgnlUtilTest extends XWorkTestCase { Exception expected = null; try { - ognlUtil.setExcludedClasses(Object.class.getName()); + ognlUtil.addExcludedClasses(Object.class.getName()); ognlUtil.setValue("class['classLoader']['defaultAssertionStatus']", ognlUtil.createDefaultContext(foo), foo, true); fail(); } catch (OgnlException e) { @@ -716,7 +716,7 @@ public class OgnlUtilTest extends XWorkTestCase { Exception expected = null; try { - ognlUtil.setExcludedClasses(Object.class.getName()); + ognlUtil.addExcludedClasses(Object.class.getName()); ognlUtil.setValue("class[\"classLoader\"]['defaultAssertionStatus']", ognlUtil.createDefaultContext(foo), foo, true); fail(); } catch (OgnlException e) { @@ -762,7 +762,7 @@ public class OgnlUtilTest extends XWorkTestCase { Exception expected = null; try { - ognlUtil.setExcludedClasses(Runtime.class.getName()); + ognlUtil.addExcludedClasses(Runtime.class.getName()); ognlUtil.setValue("@java.lang.Runtime@getRuntime().exec('mate')", ognlUtil.createDefaultContext(foo), foo, true); fail(); } catch (OgnlException e) { From c2b2511f851da37055df2b13fb7405afe5f316e2 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 20 Jun 2018 11:47:53 +0200 Subject: [PATCH 10/35] Adds proper handling of primitive types --- .../xwork2/ognl/SecurityMemberAccess.java | 11 +++-- core/src/main/resources/struts-default.xml | 2 +- .../xwork2/ognl/SecurityMemberAccessTest.java | 46 ++++++++++++------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index 05db70fc9..5d87944bc 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -154,9 +154,9 @@ public class SecurityMemberAccess implements MemberAccess { if (targetPackage == null || memberPackage == null) { LOG.warn("The use of the default (unnamed) package is discouraged!"); } - - final String targetPackageName = targetPackage == null ? "" : targetPackage.getName(); - final String memberPackageName = memberPackage == null ? "" : memberPackage.getName(); + + String targetPackageName = targetPackage == null ? "" : targetPackage.getName(); + String memberPackageName = memberPackage == null ? "" : memberPackage.getName(); for (Pattern pattern : excludedPackageNamePatterns) { if (pattern.matcher(targetPackageName).matches() || pattern.matcher(memberPackageName).matches()) { @@ -164,7 +164,10 @@ public class SecurityMemberAccess implements MemberAccess { } } - for (String packageName : excludedPackageNames) { + targetPackageName = targetPackageName + "."; + memberPackageName = memberPackageName + "."; + + for (String packageName: excludedPackageNames) { if (targetPackageName.startsWith(packageName) || targetPackageName.equals(packageName) || memberPackageName.startsWith(packageName) || memberPackageName.equals(packageName)) { return true; diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 3896a51f2..417e6d82d 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -62,7 +62,7 @@ com.opensymphony.xwork2.ognl., java.lang., ognl., - javax, + javax., freemarker.core., freemarker.template., freemarker.ext.rhino., diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java index c78e9abee..ddb3f2826 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java @@ -22,7 +22,6 @@ import com.opensymphony.xwork2.util.TextParseUtil; import junit.framework.TestCase; import java.lang.reflect.Member; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -62,7 +61,7 @@ public class SecurityMemberAccessTest extends TestCase { String propertyName = "stringField"; Member member = FooBar.class.getDeclaredMethod("get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1)); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(FooBar.class); sma.setExcludedClasses(excluded); @@ -108,7 +107,7 @@ public class SecurityMemberAccessTest extends TestCase { String propertyName = "barLogic"; Member member = BarInterface.class.getMethod(propertyName); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(BarInterface.class); sma.setExcludedClasses(excluded); @@ -126,7 +125,7 @@ public class SecurityMemberAccessTest extends TestCase { String propertyName = "fooLogic"; Member member = FooBar.class.getMethod(propertyName); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(BarInterface.class); sma.setExcludedClasses(excluded); @@ -158,7 +157,7 @@ public class SecurityMemberAccessTest extends TestCase { String propertyName = "barLogic"; Member member = BarInterface.class.getMethod(propertyName); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(FooBarInterface.class); sma.setExcludedClasses(excluded); @@ -173,7 +172,7 @@ public class SecurityMemberAccessTest extends TestCase { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set excluded = new HashSet(); + Set excluded = new HashSet<>(); excluded.add(Pattern.compile("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*")); sma.setExcludedPackageNamePatterns(excluded); @@ -191,7 +190,7 @@ public class SecurityMemberAccessTest extends TestCase { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set excluded = new HashSet(); + Set excluded = new HashSet<>(); excluded.add(FooBar.class.getPackage().getName()); sma.setExcludedPackageNames(excluded); @@ -205,11 +204,11 @@ public class SecurityMemberAccessTest extends TestCase { assertFalse("stringField is accessible!", actual); } - public void testDefaultPackageExclusion() throws Exception { + public void testDefaultPackageExclusion() { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set excluded = new HashSet(); + Set excluded = new HashSet<>(); excluded.add(Pattern.compile("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*")); sma.setExcludedPackageNamePatterns(excluded); @@ -220,11 +219,11 @@ public class SecurityMemberAccessTest extends TestCase { assertFalse("default package is excluded!", actual); } - public void testDefaultPackageExclusion2() throws Exception { + public void testDefaultPackageExclusion2() { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set excluded = new HashSet(); + Set excluded = new HashSet<>(); excluded.add(Pattern.compile("^$")); sma.setExcludedPackageNamePatterns(excluded); @@ -317,10 +316,10 @@ public class SecurityMemberAccessTest extends TestCase { public void testAccessPrimitiveDoubleWithNames() throws Exception { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - sma.setExcludedPackageNames(TextParseUtil.commaDelimitedStringToSet("java.lang.,ognl,javax")); + sma.setExcludedPackageNames(TextParseUtil.commaDelimitedStringToSet("ognl.,javax.")); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(Object.class); excluded.add(Runtime.class); excluded.add(System.class); @@ -369,7 +368,7 @@ public class SecurityMemberAccessTest extends TestCase { public void testAccessPrimitiveDoubleWithPackageRegExs() throws Exception { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set patterns = new HashSet(); + Set patterns = new HashSet<>(); patterns.add(Pattern.compile("^java\\.lang\\..*")); sma.setExcludedPackageNamePatterns(patterns); @@ -386,7 +385,7 @@ public class SecurityMemberAccessTest extends TestCase { public void testAccessMemberAccessIsAccessible() throws Exception { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(ognl.MemberAccess.class); sma.setExcludedClasses(excluded); @@ -404,7 +403,7 @@ public class SecurityMemberAccessTest extends TestCase { public void testAccessMemberAccessIsBlocked() throws Exception { // given SecurityMemberAccess sma = new SecurityMemberAccess(false); - Set> excluded = new HashSet>(); + Set> excluded = new HashSet<>(); excluded.add(SecurityMemberAccess.class); sma.setExcludedClasses(excluded); @@ -419,6 +418,21 @@ public class SecurityMemberAccessTest extends TestCase { assertFalse(accessible); } + public void testPackageNameExclusionAsCommaDelimited() { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(false); + + + sma.setExcludedPackageNames(TextParseUtil.commaDelimitedStringToSet("java.lang.")); + + // when + boolean actual = sma.isPackageExcluded(String.class.getPackage(), null); + actual &= sma.isPackageExcluded(null, String.class.getPackage()); + + // then + assertTrue("package java.lang. is accessible!", actual); + } + } class FooBar implements FooBarInterface { From 9b4301c495c882d233417c14c65ac0061ab6ca67 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 21 Jun 2018 08:20:11 +0200 Subject: [PATCH 11/35] Adds more general exclusion --- core/src/main/resources/struts-default.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 417e6d82d..84605fa05 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -49,7 +49,7 @@ ognl.ClassResolver, ognl.TypeConverter, ognl.MemberAccess, - com.opensymphony.xwork2.ognl.SecurityMemberAccess, + ognl.DefaultMemberAccess, com.opensymphony.xwork2.ActionContext" /> @@ -69,7 +69,9 @@ freemarker.ext.beans., sun.misc., sun.reflect., - javassist." /> + javassist., + com.opensymphony.xwork2.ognl., + com.opensymphony.xwork2.security." /> From eec0d8e877dc86da4946268caf73c2f7ed5d2fc6 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 21 Jun 2018 09:19:46 +0200 Subject: [PATCH 12/35] Validates action, namespace and method in the same way --- .../org/apache/struts2/StrutsConstants.java | 5 + .../mapper/DefaultActionMapper.java | 35 +++- .../mapper/DefaultActionMapperTest.java | 197 ++++++++++-------- .../apache/struts2/rest/RestActionMapper.java | 2 +- 4 files changed, 150 insertions(+), 89 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index d01e15088..81a4f803a 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -282,6 +282,11 @@ public final class StrutsConstants { public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser"; + /** namespaces names' whitelist **/ + public static final String STRUTS_ALLOWED_NAMESPACE_NAMES = "struts.allowed.namespace.names"; + /** default namespace name to use when namespace didn't match the whitelist **/ + public static final String STRUTS_DEFAULT_NAMESPACE_NAME = "struts.default.namespace.name"; + /** actions names' whitelist **/ public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names"; /** default action name to use when action didn't match the whitelist **/ diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java index 224e36a7c..95bd5c163 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java @@ -31,7 +31,6 @@ import org.apache.logging.log4j.Logger; import org.apache.struts2.RequestUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsConstants; -import org.apache.struts2.StrutsException; import org.apache.struts2.util.PrefixTrie; import javax.servlet.http.HttpServletRequest; @@ -117,6 +116,10 @@ public class DefaultActionMapper implements ActionMapper { protected boolean allowSlashesInActionNames = false; protected boolean alwaysSelectFullNamespace = false; protected PrefixTrie prefixTrie = null; + + protected Pattern allowedNamespaceNames = Pattern.compile("[a-zA-Z0-9._/\\-]*"); + protected String defaultNamespaceName = "/"; + protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*"); protected String defaultActionName = "index"; @@ -202,6 +205,16 @@ public class DefaultActionMapper implements ActionMapper { this.alwaysSelectFullNamespace = BooleanUtils.toBoolean(alwaysSelectFullNamespace); } + @Inject(value = StrutsConstants.STRUTS_ALLOWED_NAMESPACE_NAMES, required = false) + public void setAllowedNamespaceNames(String allowedNamespaceNames) { + this.allowedNamespaceNames = Pattern.compile(allowedNamespaceNames); + } + + @Inject(value = StrutsConstants.STRUTS_DEFAULT_NAMESPACE_NAME, required = false) + public void setDefaultNamespaceName(String defaultNamespaceName) { + this.defaultNamespaceName = defaultNamespaceName; + } + @Inject(value = StrutsConstants.STRUTS_ALLOWED_ACTION_NAMES, required = false) public void setAllowedActionNames(String allowedActionNames) { this.allowedActionNames = Pattern.compile(allowedActionNames); @@ -389,10 +402,28 @@ public class DefaultActionMapper implements ActionMapper { } } - mapping.setNamespace(namespace); + mapping.setNamespace(cleanupNamespaceName(namespace)); mapping.setName(cleanupActionName(name)); } + /** + * Checks namespace name against allowed pattern if not matched returns default namespace + * + * @param rawNamespace name extracted from URI + * @return safe namespace name + */ + protected String cleanupNamespaceName(final String rawNamespace) { + if (allowedNamespaceNames.matcher(rawNamespace).matches()) { + return rawNamespace; + } else { + LOG.warn( + "{} did not match allowed namespace names {} - default namespace {} will be used!", + rawNamespace, allowedActionNames, defaultActionName + ); + return defaultNamespaceName; + } + } + /** * Checks action name against allowed pattern if not matched returns default action name * diff --git a/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java b/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java index 3c012e8a8..479494b16 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java @@ -27,12 +27,12 @@ import com.opensymphony.xwork2.config.entities.PackageConfig; import com.opensymphony.xwork2.config.impl.DefaultConfiguration; import com.opensymphony.xwork2.inject.Container; import org.apache.struts2.ServletActionContext; -import org.apache.struts2.StrutsException; import org.apache.struts2.StrutsInternalTestCase; import org.apache.struts2.result.StrutsResultSupport; import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -65,7 +65,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { }; } - public void testGetMapping() throws Exception { + public void testGetMapping() { req.setupGetRequestURI("/my/namespace/actionName.action"); req.setupGetServletPath("/my/namespace/actionName.action"); req.setupGetAttribute(null); @@ -79,7 +79,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(mapping.getMethod()); } - public void testGetMappingWithMethod() throws Exception { + public void testGetMappingWithMethod() { req.setupGetParameterMap(new HashMap()); req.setupGetRequestURI("/my/namespace/actionName!add.action"); req.setupGetServletPath("/my/namespace/actionName!add.action"); @@ -95,7 +95,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("add", mapping.getMethod()); } - public void testGetMappingWithSlashedName() throws Exception { + public void testGetMappingWithSlashedName() { req.setupGetRequestURI("/my/foo/actionName.action"); req.setupGetServletPath("/my/foo/actionName.action"); @@ -111,7 +111,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(mapping.getMethod()); } - public void testGetMappingWithSlashedNameAtRootButNoSlashPackage() throws Exception { + public void testGetMappingWithSlashedNameAtRootButNoSlashPackage() { req.setupGetRequestURI("/foo/actionName.action"); req.setupGetServletPath("/foo/actionName.action"); @@ -127,7 +127,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(mapping.getMethod()); } - public void testGetMappingWithSlashedNameAtRoot() throws Exception { + public void testGetMappingWithSlashedNameAtRoot() { config = new DefaultConfiguration(); PackageConfig pkg = new PackageConfig.Builder("myns") .namespace("/my/namespace").build(); @@ -158,7 +158,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { - public void testGetMappingWithNamespaceSlash() throws Exception { + public void testGetMappingWithNamespaceSlash() { req.setupGetRequestURI("/my-hh/abc.action"); req.setupGetServletPath("/my-hh/abc.action"); @@ -181,7 +181,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("my-hh/abc", mapping.getName()); } - public void testGetMappingWithUnknownNamespace() throws Exception { + public void testGetMappingWithUnknownNamespace() { req.setupGetRequestURI("/bo/foo/actionName.action"); req.setupGetServletPath("/bo/foo/actionName.action"); req.setupGetAttribute(null); @@ -195,7 +195,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(mapping.getMethod()); } - public void testGetMappingWithUnknownNamespaceButFullNamespaceSelect() throws Exception { + public void testGetMappingWithUnknownNamespaceButFullNamespaceSelect() { req.setupGetRequestURI("/bo/foo/actionName.action"); req.setupGetServletPath("/bo/foo/actionName.action"); req.setupGetAttribute(null); @@ -210,7 +210,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(mapping.getMethod()); } - public void testGetMappingWithActionName_methodAndName() throws Exception { + public void testGetMappingWithActionName_methodAndName() { DefaultActionMapper mapper = new DefaultActionMapper(); mapper.setAllowDynamicMethodCalls("true"); ActionMapping mapping = mapper.getMappingFromActionName("actionName!add"); @@ -218,24 +218,24 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("add", mapping.getMethod()); } - public void testGetMappingWithActionName_name() throws Exception { + public void testGetMappingWithActionName_name() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping mapping = mapper.getMappingFromActionName("actionName"); assertEquals("actionName", mapping.getName()); - assertEquals(null, mapping.getMethod()); + assertNull(mapping.getMethod()); } - public void testGetMappingWithActionName_noDynamicMethod() throws Exception { + public void testGetMappingWithActionName_noDynamicMethod() { DefaultActionMapper mapper = new DefaultActionMapper(); mapper.setAllowDynamicMethodCalls("false"); ActionMapping mapping = mapper.getMappingFromActionName("actionName!add"); assertEquals("actionName!add", mapping.getName()); - assertEquals(null, mapping.getMethod()); + assertNull(mapping.getMethod()); } - public void testGetMappingWithActionName_noDynamicMethodColonPrefix() throws Exception { + public void testGetMappingWithActionName_noDynamicMethodColonPrefix() { - Map parameterMap = new HashMap(); + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.METHOD_PREFIX + "someMethod", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -247,16 +247,16 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); assertEquals("someServletPath", actionMapping.getName()); - assertEquals(null, actionMapping.getMethod()); + assertNull(actionMapping.getMethod()); } - public void testGetMappingWithActionName_null() throws Exception { + public void testGetMappingWithActionName_null() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping mapping = mapper.getMappingFromActionName(null); assertNull(mapping); } - public void testGetUri() throws Exception { + public void testGetUri() { req.setupGetParameterMap(new HashMap()); req.setupGetRequestURI("/my/namespace/actionName.action"); req.setupGetServletPath("/my/namespace/actionName.action"); @@ -268,7 +268,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/my/namespace/actionName.action", mapper.getUriFromActionMapping(mapping)); } - public void testGetUriWithSemicolonPresent() throws Exception { + public void testGetUriWithSemicolonPresent() { req.setupGetParameterMap(new HashMap()); req.setupGetRequestURI("/my/namespace/actionName.action;abc=123rty56"); req.setupGetServletPath("/my/namespace/actionName.action;abc=123rty56"); @@ -280,7 +280,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/my/namespace/actionName.action", mapper.getUriFromActionMapping(mapping)); } - public void testGetUriWithMethod() throws Exception { + public void testGetUriWithMethod() { req.setupGetParameterMap(new HashMap()); req.setupGetRequestURI("/my/namespace/actionName!add.action"); req.setupGetServletPath("/my/namespace/actionName!add.action"); @@ -293,8 +293,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/my/namespace/actionName!add.action", mapper.getUriFromActionMapping(mapping)); } - public void testGetUriWithOriginalExtension() throws Exception { - ActionMapping mapping = new ActionMapping("actionName", "/ns", null, new HashMap()); + public void testGetUriWithOriginalExtension() { + ActionMapping mapping = new ActionMapping("actionName", "/ns", null, new HashMap()); ActionMapping orig = new ActionMapping(); orig.setExtension("foo"); @@ -304,7 +304,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/ns/actionName.foo", mapper.getUriFromActionMapping(mapping)); } - public void testGetMappingWithNoExtension() throws Exception { + public void testGetMappingWithNoExtension() { req.setupGetParameterMap(new HashMap()); req.setupGetRequestURI("/my/namespace/actionName"); req.setupGetServletPath("/my/namespace/actionName"); @@ -320,7 +320,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(mapping.getMethod()); } - public void testGetMappingWithNoExtensionButUriHasExtension() throws Exception { + public void testGetMappingWithNoExtensionButUriHasExtension() { req.setupGetParameterMap(new HashMap()); req.setupGetRequestURI("/my/namespace/actionName.html"); req.setupGetServletPath("/my/namespace/actionName.html"); @@ -340,7 +340,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { // === test name & namespace === // ============================= - public void testParseNameAndNamespace1() throws Exception { + public void testParseNameAndNamespace1() { ActionMapping actionMapping = new ActionMapping(); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); @@ -350,7 +350,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals(actionMapping.getNamespace(), ""); } - public void testParseNameAndNamespace2() throws Exception { + public void testParseNameAndNamespace2() { ActionMapping actionMapping = new ActionMapping(); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); @@ -360,7 +360,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals(actionMapping.getNamespace(), "/"); } - public void testParseNameAndNamespace3() throws Exception { + public void testParseNameAndNamespace3() { ActionMapping actionMapping = new ActionMapping(); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); @@ -370,7 +370,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals(actionMapping.getNamespace(), "/my"); } - public void testParseNameAndNamespace_NoSlashes() throws Exception { + public void testParseNameAndNamespace_NoSlashes() { ActionMapping actionMapping = new ActionMapping(); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); @@ -381,7 +381,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals(actionMapping.getNamespace(), ""); } - public void testParseNameAndNamespace_AllowSlashes() throws Exception { + public void testParseNameAndNamespace_AllowSlashes() { ActionMapping actionMapping = new ActionMapping(); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); @@ -397,8 +397,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { // === test special prefix === // =========================== - public void testActionPrefixWhenDisabled() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefixWhenDisabled() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -411,8 +411,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("someServletPath", actionMapping.getName()); } - public void testActionPrefixWhenEnabled() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefixWhenEnabled() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -426,8 +426,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("myAction", actionMapping.getName()); } - public void testActionPrefixWhenSlashesAndCrossNamespaceDisabled() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefixWhenSlashesAndCrossNamespaceDisabled() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "my/Action", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -442,8 +442,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("my/Action", actionMapping.getName()); } - public void testActionPrefixWhenSlashesButSlashesDisabledAndCrossNamespaceDisabled() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefixWhenSlashesButSlashesDisabledAndCrossNamespaceDisabled() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "my/Action", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -458,8 +458,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("Action", actionMapping.getName()); } - public void testActionPrefixWhenSlashesButSlashesDisabledAndCrossNamespace() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefixWhenSlashesButSlashesDisabledAndCrossNamespace() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "my/Action", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -475,8 +475,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("my/Action", actionMapping.getName()); } - public void testActionPrefixWhenCrossNamespace() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefixWhenCrossNamespace() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "/my/Action", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -491,8 +491,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/my/Action", actionMapping.getName()); } - public void testActionPrefix_fromImageButton() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefix_fromImageButton() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction", ""); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction.x", ""); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction.y", ""); @@ -508,8 +508,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("myAction", actionMapping.getName()); } - public void testActionPrefix_fromIEImageButton() throws Exception { - Map parameterMap = new HashMap(); + public void testActionPrefix_fromIEImageButton() { + Map parameterMap = new HashMap<>(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction.x", ""); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction.y", ""); @@ -524,8 +524,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("myAction", actionMapping.getName()); } - public void testRedirectPrefix() throws Exception { - Map parameterMap = new HashMap(); + public void testRedirectPrefix() { + Map parameterMap = new HashMap<>(); parameterMap.put("redirect:" + "http://www.google.com", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -540,8 +540,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(result); } - public void testUnsafeRedirectPrefix() throws Exception { - Map parameterMap = new HashMap(); + public void testUnsafeRedirectPrefix() { + Map parameterMap = new HashMap<>(); parameterMap.put("redirect:" + "http://%{3*4}", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -556,8 +556,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(result); } - public void testRedirectActionPrefix() throws Exception { - Map parameterMap = new HashMap(); + public void testRedirectActionPrefix() { + Map parameterMap = new HashMap<>(); parameterMap.put("redirectAction:" + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -573,8 +573,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(result); } - public void testUnsafeRedirectActionPrefix() throws Exception { - Map parameterMap = new HashMap(); + public void testUnsafeRedirectActionPrefix() { + Map parameterMap = new HashMap<>(); parameterMap.put("redirectAction:" + "%{3*4}", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -590,8 +590,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(result); } - public void testRedirectActionPrefixWithEmptyExtension() throws Exception { - Map parameterMap = new HashMap(); + public void testRedirectActionPrefixWithEmptyExtension() { + Map parameterMap = new HashMap<>(); parameterMap.put("redirectAction:" + "myAction", ""); StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -608,8 +608,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertNull(result); } - public void testCustomActionPrefix() throws Exception { - Map parameterMap = new HashMap(); + public void testCustomActionPrefix() { + Map parameterMap = new HashMap<>(); parameterMap.put("foo:myAction", ""); final StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); @@ -627,39 +627,39 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals(actionMapping.getName(), "myAction"); } - public void testDropExtension() throws Exception { + public void testDropExtension() { DefaultActionMapper mapper = new DefaultActionMapper(); String name = mapper.dropExtension("foo.action", new ActionMapping()); - assertTrue("Name not right: "+name, "foo".equals(name)); + assertEquals("Name not right: " + name, "foo", name); name = mapper.dropExtension("foo.action.action", new ActionMapping()); - assertTrue("Name not right: "+name, "foo.action".equals(name)); + assertEquals("Name not right: " + name, "foo.action", name); } - public void testDropExtensionWhenBlank() throws Exception { + public void testDropExtensionWhenBlank() { DefaultActionMapper mapper = new DefaultActionMapper(); mapper.setExtensions("action,,"); String name = mapper.dropExtension("foo.action", new ActionMapping()); - assertTrue("Name not right: "+name, "foo".equals(name)); + assertEquals("Name not right: " + name, "foo", name); name = mapper.dropExtension("foo", new ActionMapping()); - assertTrue("Name not right: "+name, "foo".equals(name)); + assertEquals("Name not right: " + name, "foo", name); assertNull(mapper.dropExtension("foo.bar", new ActionMapping())); assertNull(mapper.dropExtension("foo.", new ActionMapping())); } - public void testDropExtensionEmbeddedDot() throws Exception { + public void testDropExtensionEmbeddedDot() { DefaultActionMapper mapper = new DefaultActionMapper(); mapper.setExtensions("action,,"); String name = mapper.dropExtension("/foo/bar-1.0/baz.action", new ActionMapping()); - assertTrue("Name not right: "+name, "/foo/bar-1.0/baz".equals(name)); + assertEquals("Name not right: " + name, "/foo/bar-1.0/baz", name); name = mapper.dropExtension("/foo/bar-1.0/baz", new ActionMapping()); - assertTrue("Name not right: "+name, "/foo/bar-1.0/baz".equals(name)); + assertEquals("Name not right: " + name, "/foo/bar-1.0/baz", name); } - public void testGetUriFromActionMapper1() throws Exception { + public void testGetUriFromActionMapper1() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -670,7 +670,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myNamespace/myActionName!myMethod.action", uri); } - public void testGetUriFromActionMapper2() throws Exception { + public void testGetUriFromActionMapper2() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -681,7 +681,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName!myMethod.action", uri); } - public void testGetUriFromActionMapper3() throws Exception { + public void testGetUriFromActionMapper3() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -693,7 +693,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { } - public void testGetUriFromActionMapper4() throws Exception { + public void testGetUriFromActionMapper4() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setName("myActionName"); @@ -703,7 +703,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName.action", uri); } - public void testGetUriFromActionMapper5() throws Exception { + public void testGetUriFromActionMapper5() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setName("myActionName"); @@ -714,7 +714,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { } // - public void testGetUriFromActionMapper6() throws Exception { + public void testGetUriFromActionMapper6() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -725,7 +725,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myNamespace/myActionName!myMethod.action?test=bla", uri); } - public void testGetUriFromActionMapper7() throws Exception { + public void testGetUriFromActionMapper7() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -736,7 +736,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName!myMethod.action?test=bla", uri); } - public void testGetUriFromActionMapper8() throws Exception { + public void testGetUriFromActionMapper8() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -748,7 +748,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { } - public void testGetUriFromActionMapper9() throws Exception { + public void testGetUriFromActionMapper9() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setName("myActionName?test=bla"); @@ -758,7 +758,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName.action?test=bla", uri); } - public void testGetUriFromActionMapper10() throws Exception { + public void testGetUriFromActionMapper10() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setName("myActionName?test=bla"); @@ -768,7 +768,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName.action?test=bla", uri); } - public void testGetUriFromActionMapper11() throws Exception { + public void testGetUriFromActionMapper11() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setName("myActionName.action"); @@ -778,7 +778,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName.action", uri); } - public void testGetUriFromActionMapper12() throws Exception { + public void testGetUriFromActionMapper12() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setName("myActionName.action"); @@ -788,7 +788,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myActionName.action", uri); } - public void testGetUriFromActionMapper_justActionAndMethod() throws Exception { + public void testGetUriFromActionMapper_justActionAndMethod() { DefaultActionMapper mapper = new DefaultActionMapper(); ActionMapping actionMapping = new ActionMapping(); actionMapping.setMethod("myMethod"); @@ -799,7 +799,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("myActionName!myMethod", uri); } - public void testGetUriFromActionMapperWhenBlankExtension() throws Exception { + public void testGetUriFromActionMapperWhenBlankExtension() { DefaultActionMapper mapper = new DefaultActionMapper(); mapper.setExtensions(",,"); ActionMapping actionMapping = new ActionMapping(); @@ -811,7 +811,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("/myNamespace/myActionName!myMethod", uri); } - public void testSetExtension() throws Exception { + public void testSetExtension() { DefaultActionMapper mapper = new DefaultActionMapper(); mapper.setExtensions(""); assertNull(mapper.extensions); @@ -828,15 +828,40 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals(Arrays.asList("html", "", "xml"), mapper.extensions); mapper.setExtensions("xml"); - assertEquals(Arrays.asList("xml"), mapper.extensions); + assertEquals(Collections.singletonList("xml"), mapper.extensions); mapper.setExtensions(","); - assertEquals(Arrays.asList(""), mapper.extensions); + assertEquals(Collections.singletonList(""), mapper.extensions); } - public void testAllowedActionNames() throws Exception { + public void testAllowedNamespaceNames() { + DefaultActionMapper mapper = new DefaultActionMapper(); + + String namespace = "/"; + assertEquals(namespace, mapper.cleanupNamespaceName(namespace)); + + namespace = "${namespace}"; + assertEquals(mapper.defaultNamespaceName, mapper.cleanupNamespaceName(namespace)); + + namespace = "${${%{namespace}}}"; + assertEquals(mapper.defaultNamespaceName, mapper.cleanupNamespaceName(namespace)); + + namespace = "${#foo='namespace',#foo}"; + assertEquals(mapper.defaultNamespaceName, mapper.cleanupNamespaceName(namespace)); + + namespace = "/test-namespace/namespace/"; + assertEquals("/test-namespace/namespace/", mapper.cleanupNamespaceName(namespace)); + + namespace = "/test_namespace/namespace-test/"; + assertEquals("/test_namespace/namespace-test/", mapper.cleanupNamespaceName(namespace)); + + namespace = "/test_namespace/namespace.test/"; + assertEquals("/test_namespace/namespace.test/", mapper.cleanupActionName(namespace)); + } + + public void testAllowedActionNames() { DefaultActionMapper mapper = new DefaultActionMapper(); String actionName = "action"; @@ -861,7 +886,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase { assertEquals("test!bar.action", mapper.cleanupActionName(actionName)); } - public void testAllowedMethodNames() throws Exception { + public void testAllowedMethodNames() { DefaultActionMapper mapper = new DefaultActionMapper(); assertEquals("", mapper.cleanupMethodName("")); diff --git a/plugins/rest/src/main/java/org/apache/struts2/rest/RestActionMapper.java b/plugins/rest/src/main/java/org/apache/struts2/rest/RestActionMapper.java index 61fef60c2..eec8bf37e 100644 --- a/plugins/rest/src/main/java/org/apache/struts2/rest/RestActionMapper.java +++ b/plugins/rest/src/main/java/org/apache/struts2/rest/RestActionMapper.java @@ -364,7 +364,7 @@ public class RestActionMapper extends DefaultActionMapper { name = uri.substring(namespace.length() + 1); } - mapping.setNamespace(namespace); + mapping.setNamespace(cleanupNamespaceName(namespace)); mapping.setName(name); } From 6af8531ceae159bca27b9c5bb2e41aa39a0e86ec Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Thu, 21 Jun 2018 12:00:46 +0430 Subject: [PATCH 13/35] delete redundant code for performance --- .../opensymphony/xwork2/ognl/SecurityMemberAccess.java | 3 +-- core/src/main/resources/struts-default.xml | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index 5d87944bc..9a04eacce 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -168,8 +168,7 @@ public class SecurityMemberAccess implements MemberAccess { memberPackageName = memberPackageName + "."; for (String packageName: excludedPackageNames) { - if (targetPackageName.startsWith(packageName) || targetPackageName.equals(packageName) - || memberPackageName.startsWith(packageName) || memberPackageName.equals(packageName)) { + if (targetPackageName.startsWith(packageName) || memberPackageName.startsWith(packageName)) { return true; } } diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 84605fa05..abd6c657d 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -45,11 +45,6 @@ java.lang.ClassLoader, java.lang.Shutdown, java.lang.ProcessBuilder, - ognl.OgnlContext, - ognl.ClassResolver, - ognl.TypeConverter, - ognl.MemberAccess, - ognl.DefaultMemberAccess, com.opensymphony.xwork2.ActionContext" /> @@ -71,7 +66,8 @@ sun.reflect., javassist., com.opensymphony.xwork2.ognl., - com.opensymphony.xwork2.security." /> + com.opensymphony.xwork2.security., + com.opensymphony.xwork2.util." /> From e88f1fa45fcf45b29a218fb09d22b05a0c94b8ea Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 27 Jun 2018 08:15:21 +0200 Subject: [PATCH 14/35] Fixes how dependencies are injected into constructor --- .../opensymphony/xwork2/ognl/OgnlUtil.java | 49 +++++++++---------- core/src/main/resources/struts-default.xml | 4 -- .../xwork2/ognl/OgnlUtilTest.java | 10 ++-- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java index bb0d4e825..12d3ed14a 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -69,26 +69,10 @@ public class OgnlUtil { private boolean allowStaticMethodAccess; private boolean disallowProxyMemberAccess; - public OgnlUtil( - @Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false) - String commaDelimitedClasses, - @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) - String commaDelimitedPackagePatterns, - @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false) - String commaDelimitedPackageNames - ) { - excludedClasses = Collections.unmodifiableSet(parseExcludedClasses(commaDelimitedClasses)); - excludedPackageNamePatterns = Collections.unmodifiableSet(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns)); - excludedPackageNames = Collections.unmodifiableSet(parseExcludedPackageNames(commaDelimitedPackageNames)); - } - - /** - * Constructor used by internal DI - */ public OgnlUtil() { - excludedClasses = Collections.emptySet(); - excludedPackageNamePatterns = Collections.emptySet(); - excludedPackageNames = Collections.emptySet(); + excludedClasses = new HashSet<>(); + excludedPackageNamePatterns = new HashSet<>(); + excludedPackageNames = new HashSet<>(); } @Inject @@ -115,6 +99,13 @@ public class OgnlUtil { } } + @Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false) + public void setExcludedClasses(String commaDelimitedClasses) { + Set> excludedClasses = this.excludedClasses; + excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses)); + this.excludedClasses = Collections.unmodifiableSet(excludedClasses); + } + private Set> parseExcludedClasses(String commaDelimitedClasses) { Set classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses); Set> classes = new HashSet<>(); @@ -130,6 +121,12 @@ public class OgnlUtil { return classes; } + @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) + public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) { + Set excludedPackageNamePatterns = this.excludedPackageNamePatterns; + excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns)); + this.excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns); + } private Set parseExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) { Set packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns); @@ -142,6 +139,13 @@ public class OgnlUtil { return packageNamePatterns; } + @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false) + public void setExcludedPackageNames(String commaDelimitedPackageNames) { + Set excludedPackageNames = this.excludedPackageNames; + excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames)); + this.excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames); + } + private Set parseExcludedPackageNames(String commaDelimitedPackageNames) { return TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames); } @@ -699,13 +703,6 @@ public class OgnlUtil { return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter); } - protected void addExcludedClasses(String commaDelimitedClasses) { - Set> existingClasses = new HashSet<>(excludedClasses); - existingClasses.addAll(parseExcludedClasses(commaDelimitedClasses)); - - excludedClasses = Collections.unmodifiableSet(existingClasses); - } - private interface OgnlTask { T execute(Object tree) throws OgnlException; } diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index abd6c657d..15cc5f9f7 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -54,15 +54,11 @@ Date: Fri, 29 Jun 2018 16:28:23 +0430 Subject: [PATCH 15/35] add an extra unit test --- .../opensymphony/xwork2/ognl/OgnlUtil.java | 9 ++- .../xwork2/ognl/OgnlUtilStrutsTest.java | 56 +++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java index 12d3ed14a..483222447 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -101,7 +101,8 @@ public class OgnlUtil { @Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false) public void setExcludedClasses(String commaDelimitedClasses) { - Set> excludedClasses = this.excludedClasses; + Set> excludedClasses = new HashSet<>(); + excludedClasses.addAll(this.excludedClasses); excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses)); this.excludedClasses = Collections.unmodifiableSet(excludedClasses); } @@ -123,7 +124,8 @@ public class OgnlUtil { @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) { - Set excludedPackageNamePatterns = this.excludedPackageNamePatterns; + Set excludedPackageNamePatterns = new HashSet<>(); + excludedPackageNamePatterns.addAll(this.excludedPackageNamePatterns); excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns)); this.excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns); } @@ -141,7 +143,8 @@ public class OgnlUtil { @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false) public void setExcludedPackageNames(String commaDelimitedPackageNames) { - Set excludedPackageNames = this.excludedPackageNames; + Set excludedPackageNames = new HashSet<>(); + excludedPackageNames.addAll(this.excludedPackageNames); excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames)); this.excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames); } diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java new file mode 100644 index 000000000..bd371dd8c --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.opensymphony.xwork2.ognl; + +import org.apache.struts2.StrutsInternalTestCase; + +public class OgnlUtilStrutsTest extends StrutsInternalTestCase { + + private OgnlUtil ognlUtil; + + @Override + public void setUp() throws Exception { + super.setUp(); + ognlUtil = container.getInstance(OgnlUtil.class); + } + + public void testDefaultExcludes() { + ognlUtil.setExcludedClasses(""); + ognlUtil.setExcludedPackageNames(""); + ognlUtil.setExcludedPackageNamePatterns(""); + assertTrue(ognlUtil.getExcludedClasses().size() > 0); + assertTrue(ognlUtil.getExcludedPackageNames().size() > 0); + + try { + ognlUtil.getExcludedClasses().clear(); + } catch (Exception ex){ + assertTrue(ex instanceof UnsupportedOperationException); + } + try { + ognlUtil.getExcludedPackageNames().clear(); + } catch (Exception ex){ + assertTrue(ex instanceof UnsupportedOperationException); + } + try { + ognlUtil.getExcludedPackageNamePatterns().clear(); + } catch (Exception ex){ + assertTrue(ex instanceof UnsupportedOperationException); + } + } +} From 0fb7daf09512d3919cc367dda3eb1064d5fcdbcc Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 24 Aug 2018 10:34:06 +0200 Subject: [PATCH 16/35] Fixes conflicts after cherry-picked changes from 2.5.x branch --- .../main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java index 483222447..c44cf6f4a 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -99,7 +99,7 @@ public class OgnlUtil { } } - @Inject(value = XWorkConstants.OGNL_EXCLUDED_CLASSES, required = false) + @Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false) public void setExcludedClasses(String commaDelimitedClasses) { Set> excludedClasses = new HashSet<>(); excludedClasses.addAll(this.excludedClasses); @@ -122,7 +122,7 @@ public class OgnlUtil { return classes; } - @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) + @Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false) public void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) { Set excludedPackageNamePatterns = new HashSet<>(); excludedPackageNamePatterns.addAll(this.excludedPackageNamePatterns); @@ -141,7 +141,7 @@ public class OgnlUtil { return packageNamePatterns; } - @Inject(value = XWorkConstants.OGNL_EXCLUDED_PACKAGE_NAMES, required = false) + @Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false) public void setExcludedPackageNames(String commaDelimitedPackageNames) { Set excludedPackageNames = new HashSet<>(); excludedPackageNames.addAll(this.excludedPackageNames); From 6f4d6feee8411134ce42d66efac239f7a0e0c7c6 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 24 Aug 2018 10:34:19 +0200 Subject: [PATCH 17/35] WW-4955 Upgrades OGNL to version 3.2.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 70077a13b..0b6e79ddc 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ UTF-8 4.3.13.RELEASE - 3.2.5 + 3.2.6 5.2 3.0.8 1.0.7 From 7bb1ea25e0948b67a47d19dfca89b1f6cf2f4f45 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 24 Aug 2018 13:05:02 +0200 Subject: [PATCH 18/35] Adds additional test to check is ArrayList.size() is accessible --- .../xwork2/ognl/OgnlUtilStrutsTest.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java index bd371dd8c..9f1f49604 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java @@ -20,16 +20,19 @@ package com.opensymphony.xwork2.ognl; import org.apache.struts2.StrutsInternalTestCase; +import java.util.ArrayList; +import java.util.List; + public class OgnlUtilStrutsTest extends StrutsInternalTestCase { - + private OgnlUtil ognlUtil; - + @Override public void setUp() throws Exception { super.setUp(); ognlUtil = container.getInstance(OgnlUtil.class); } - + public void testDefaultExcludes() { ognlUtil.setExcludedClasses(""); ognlUtil.setExcludedPackageNames(""); @@ -39,18 +42,33 @@ public class OgnlUtilStrutsTest extends StrutsInternalTestCase { try { ognlUtil.getExcludedClasses().clear(); - } catch (Exception ex){ + } catch (Exception ex) { assertTrue(ex instanceof UnsupportedOperationException); } try { ognlUtil.getExcludedPackageNames().clear(); - } catch (Exception ex){ + } catch (Exception ex) { assertTrue(ex instanceof UnsupportedOperationException); } try { ognlUtil.getExcludedPackageNamePatterns().clear(); - } catch (Exception ex){ + } catch (Exception ex) { assertTrue(ex instanceof UnsupportedOperationException); } } + + public void testAccessToSizeMethod() throws Exception { + // given + List list = new ArrayList<>(); + list.add("1"); + list.add("2"); + + // when + Object value = ognlUtil.getValue("size() > 0", ognlUtil.createDefaultContext(list), list); + + // then + assertTrue(value instanceof Boolean); + assertTrue((Boolean) value); + } + } From 6b8d5735d18737f8448e95f21eae82a65dbb0aa2 Mon Sep 17 00:00:00 2001 From: Sanghyuk Jung Date: Mon, 27 Aug 2018 20:26:58 +0900 Subject: [PATCH 19/35] Fix logging messages --- .../apache/struts2/dispatcher/mapper/DefaultActionMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java index 95bd5c163..06c31d4b3 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java @@ -418,7 +418,7 @@ public class DefaultActionMapper implements ActionMapper { } else { LOG.warn( "{} did not match allowed namespace names {} - default namespace {} will be used!", - rawNamespace, allowedActionNames, defaultActionName + rawNamespace, allowedNamespaceNames, defaultNamespaceName ); return defaultNamespaceName; } From 6420f4068f8ddbc54c022fb57d3bed881934b6a6 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 29 Aug 2018 07:57:16 +0200 Subject: [PATCH 20/35] WW-4954 Adds a test case to cover accessing ArrayList --- .../xwork2/ognl/OgnlUtilStrutsTest.java | 12 +++++------- .../opensymphony/xwork2/test/TestArrayBean.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java index 9f1f49604..1bd07e5f3 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java @@ -18,11 +18,9 @@ */ package com.opensymphony.xwork2.ognl; +import com.opensymphony.xwork2.test.TestArrayBean; import org.apache.struts2.StrutsInternalTestCase; -import java.util.ArrayList; -import java.util.List; - public class OgnlUtilStrutsTest extends StrutsInternalTestCase { private OgnlUtil ognlUtil; @@ -59,12 +57,12 @@ public class OgnlUtilStrutsTest extends StrutsInternalTestCase { public void testAccessToSizeMethod() throws Exception { // given - List list = new ArrayList<>(); - list.add("1"); - list.add("2"); + TestArrayBean bean = new TestArrayBean(); + bean.getPersons().add("Alice"); + bean.getPersons().add("Mich"); // when - Object value = ognlUtil.getValue("size() > 0", ognlUtil.createDefaultContext(list), list); + Object value = ognlUtil.getValue("persons.size() > 0", ognlUtil.createDefaultContext(bean), bean); // then assertTrue(value instanceof Boolean); diff --git a/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java b/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java new file mode 100644 index 000000000..1a30f057a --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java @@ -0,0 +1,17 @@ +package com.opensymphony.xwork2.test; + +import java.util.ArrayList; +import java.util.List; + +public class TestArrayBean { + + private List persons = new ArrayList<>(); + + public void setPersons(List persons) { + this.persons = persons; + } + + public List getPersons() { + return persons; + } +} From b213d58f54d3bc89b21726d441325202ef99628d Mon Sep 17 00:00:00 2001 From: Aleksandr Mashchenko Date: Fri, 31 Aug 2018 00:05:42 +0300 Subject: [PATCH 21/35] Better logging message (for WW-4954) --- .../com/opensymphony/xwork2/ognl/SecurityMemberAccess.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index 9a04eacce..ce316f6ab 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -98,7 +98,8 @@ public class SecurityMemberAccess implements MemberAccess { } if (isPackageExcluded(targetClass.getPackage(), memberClass.getPackage())) { - LOG.warn("Package of target [{}] or package of member [{}] are excluded!", target, member); + LOG.warn("Package [{}] of target class [{}] of target [{}] or package [{}] of member [{}] are excluded!", targetClass.getPackage(), targetClass, + target, memberClass.getPackage(), member); return false; } From be1a93b0d5be48bf64927bf8c9e272d401686d7f Mon Sep 17 00:00:00 2001 From: Aleksandr Mashchenko Date: Fri, 31 Aug 2018 22:25:05 +0300 Subject: [PATCH 22/35] Improve log messages in SecurityMemberAccess --- .../xwork2/ognl/SecurityMemberAccess.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index ce316f6ab..2002669f2 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -81,17 +81,18 @@ public class SecurityMemberAccess implements MemberAccess { @Override public boolean isAccessible(Map context, Object target, Member member, String propertyName) { LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName); - + + Class targetClass = target.getClass(); + Class memberClass = member.getDeclaringClass(); + if (checkEnumAccess(target, member)) { - LOG.trace("Allowing access to enum: {}", target); + LOG.trace("Allowing access to enum: target class [{}] of target [{}], member [{}]", targetClass, target, member); return true; } - Class targetClass = target.getClass(); - Class memberClass = member.getDeclaringClass(); - if (Modifier.isStatic(member.getModifiers()) && allowStaticMethodAccess) { - LOG.debug("Support for accessing static methods [target: {}, member: {}, property: {}] is deprecated!", target, member, propertyName); + LOG.debug("Support for accessing static methods [target: {}, targetClass: {}, member: {}, property: {}] is deprecated!", + target, targetClass, member, propertyName); if (!isClassExcluded(member.getDeclaringClass())) { targetClass = member.getDeclaringClass(); } @@ -104,7 +105,7 @@ public class SecurityMemberAccess implements MemberAccess { } if (isClassExcluded(targetClass)) { - LOG.warn("Target class [{}] is excluded!", target); + LOG.warn("Target class [{}] of target [{}] is excluded!", targetClass, target); return false; } @@ -114,7 +115,7 @@ public class SecurityMemberAccess implements MemberAccess { } if (disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)) { - LOG.warn("Access to proxy [{}] is blocked!", member); + LOG.warn("Access to proxy is blocked! Target class [{}] of target [{}], member [{}]", targetClass, target, member); return false; } From fd63512318adc5cbc1f5d627e4ae0f7f257649ce Mon Sep 17 00:00:00 2001 From: Aleksandr Mashchenko Date: Sun, 2 Sep 2018 22:08:33 +0300 Subject: [PATCH 23/35] WW-4956 Upgrade to Log4j2 2.11.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b6e79ddc..9af53c5cf 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ 5.2 3.0.8 1.0.7 - 2.10.0 + 2.11.1 2.9.5 From 537a1b1abb57f340b3218c7ce43005d49be664d7 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 31 Aug 2018 11:46:59 +0200 Subject: [PATCH 24/35] Adds missing header with license --- .../xwork2/test/TestArrayBean.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java b/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java index 1a30f057a..a92a8ffd1 100644 --- a/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java +++ b/core/src/test/java/com/opensymphony/xwork2/test/TestArrayBean.java @@ -1,3 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package com.opensymphony.xwork2.test; import java.util.ArrayList; From f523a341d474eddbc6c941ddbb1d843c743603ca Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 30 Aug 2018 08:39:11 +0200 Subject: [PATCH 25/35] WW-4954 Moves XWorkList out of util package --- .../xwork2/conversion/impl/CollectionConverter.java | 1 - .../xwork2/{util => conversion/impl}/XWorkList.java | 3 +-- .../xwork2/{util => conversion/impl}/XWorkListTest.java | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) rename core/src/main/java/com/opensymphony/xwork2/{util => conversion/impl}/XWorkList.java (98%) rename core/src/test/java/com/opensymphony/xwork2/{util => conversion/impl}/XWorkListTest.java (98%) diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java index 730e4af8b..b7f707f40 100644 --- a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java +++ b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/CollectionConverter.java @@ -21,7 +21,6 @@ package com.opensymphony.xwork2.conversion.impl; import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer; import com.opensymphony.xwork2.conversion.TypeConverter; import com.opensymphony.xwork2.inject.Inject; -import com.opensymphony.xwork2.util.XWorkList; import java.lang.reflect.Member; import java.util.Collection; diff --git a/core/src/main/java/com/opensymphony/xwork2/util/XWorkList.java b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkList.java similarity index 98% rename from core/src/main/java/com/opensymphony/xwork2/util/XWorkList.java rename to core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkList.java index 666ff5f1e..38da102be 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/XWorkList.java +++ b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkList.java @@ -16,13 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package com.opensymphony.xwork2.util; +package com.opensymphony.xwork2.conversion.impl; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.XWorkException; import com.opensymphony.xwork2.conversion.TypeConverter; -import com.opensymphony.xwork2.conversion.impl.XWorkConverter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/core/src/test/java/com/opensymphony/xwork2/util/XWorkListTest.java b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkListTest.java similarity index 98% rename from core/src/test/java/com/opensymphony/xwork2/util/XWorkListTest.java rename to core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkListTest.java index 21c8f03a9..a94d63caa 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/XWorkListTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/XWorkListTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.opensymphony.xwork2.util; +package com.opensymphony.xwork2.conversion.impl; import com.opensymphony.xwork2.XWorkTestCase; From 0d209f8e0176fe1b0a22971853b342da41dc9aa8 Mon Sep 17 00:00:00 2001 From: Zack Macomber Date: Wed, 12 Sep 2018 17:36:59 -0400 Subject: [PATCH 26/35] Fixed grammar --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 280770ddc..a17ccdaee 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ to difficult problems. ## Why should you use Apache Struts? -Apache Struts is a modern, maintained and full-featured web framework. It has been there for years and given the huge user +Apache Struts is a modern, maintained and full-featured web framework. As it has been around for years and grown a huge user base it is unlikely it will go away anytime soon. Not only that, we have dedicated users and developers on the project. Apache Struts is licensed under the Apache License 2.0 and this will not change. We maintain a clean IP and you are “safe” to use the project. Sometimes you are not “safe” to use a project when a company controls the SCM. From 4714fe889c76198b8f2c14ec8924e467db9dab10 Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Thu, 6 Sep 2018 12:59:21 +0430 Subject: [PATCH 27/35] delete temp files on close instead of on JVM exists (cherry picked from commit b802baa) --- .../xwork2/util/fs/JarEntryRevision.java | 27 +++---------------- .../util/fs/StrutsJarURLConnection.java | 20 +++++++++++--- .../xwork2/util/fs/JarEntryRevisionTest.java | 18 +++++++++++++ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java b/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java index a0fea58c4..9fb05348e 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java @@ -37,9 +37,7 @@ public class JarEntryRevision extends Revision { private long lastModified; public static Revision build(URL fileUrl, FileManager fileManager) { - StrutsJarURLConnection conn = null; - try { - conn = StrutsJarURLConnection.openConnection(fileUrl); + try (StrutsJarURLConnection conn = StrutsJarURLConnection.openConnection(fileUrl)) { conn.setUseCaches(false); URL url = fileManager.normalizeToFileProtocol(fileUrl); if (url != null) { @@ -51,14 +49,6 @@ public class JarEntryRevision extends Revision { LOG.warn("Could not create JarEntryRevision for [{}]!", fileUrl, e); return null; } - finally { - if(null != conn) { - try { - conn.getInputStream().close(); - } catch (IOException ignored) { - } - } - } } private JarEntryRevision(URL jarFileURL, long lastModified) { @@ -70,21 +60,12 @@ public class JarEntryRevision extends Revision { } public boolean needsReloading() { - StrutsJarURLConnection conn = null; long lastLastModified = lastModified; - try { - conn = StrutsJarURLConnection.openConnection(jarFileURL); + try (StrutsJarURLConnection conn = StrutsJarURLConnection.openConnection(jarFileURL)) { conn.setUseCaches(false); lastLastModified = conn.getJarEntry().getTime(); - } catch (IOException ignored) { - } - finally { - if(null != conn) { - try { - conn.getInputStream().close(); - } catch (IOException ignored) { - } - } + } catch (Throwable e) { + LOG.warn("Could not check if needsReloading for [{}]!", jarFileURL, e); } return lastModified < lastLastModified; diff --git a/core/src/main/java/com/opensymphony/xwork2/util/fs/StrutsJarURLConnection.java b/core/src/main/java/com/opensymphony/xwork2/util/fs/StrutsJarURLConnection.java index 44a376aaf..e733ac7ce 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/fs/StrutsJarURLConnection.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/fs/StrutsJarURLConnection.java @@ -44,7 +44,7 @@ import java.util.jar.JarFile; * While {@link JarURLConnection#parseSpecs(URL)} is private, then we had to extend {@link URLConnection} instead * @since 2.5.15 */ -class StrutsJarURLConnection extends URLConnection { +class StrutsJarURLConnection extends URLConnection implements AutoCloseable { private static final String FILE_URL_PREFIX = "file:"; private JarURLConnection jarURLConnection; @@ -123,8 +123,8 @@ class StrutsJarURLConnection extends URLConnection { Path tmpFile = Files.createTempFile("jar_cache", null); try { Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); - JarFile jarFile = new JarFile(tmpFile.toFile(), true, JarFile.OPEN_READ); - tmpFile.toFile().deleteOnExit(); + JarFile jarFile = new JarFile(tmpFile.toFile(), true, JarFile.OPEN_READ + | JarFile.OPEN_DELETE); return jarFile; } catch (Throwable thr) { try { @@ -171,6 +171,20 @@ class StrutsJarURLConnection extends URLConnection { } } + @Override + public void close() throws Exception { + try { + getInputStream().close(); + } catch (IOException ignored) { + } + if (jarURLConnection == null) { + try { + jarFile.close(); + } catch (IOException ignored) { + } + } + } + static StrutsJarURLConnection openConnection(URL url) throws IOException { return new StrutsJarURLConnection(url); } diff --git a/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java b/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java index 54a10b90a..6969ccd3f 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java @@ -21,7 +21,9 @@ package com.opensymphony.xwork2.util.fs; import com.opensymphony.xwork2.FileManager; import com.opensymphony.xwork2.FileManagerFactory; import com.opensymphony.xwork2.XWorkTestCase; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.filefilter.WildcardFileFilter; import java.io.File; import java.io.FileOutputStream; @@ -30,6 +32,8 @@ import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; @@ -113,6 +117,20 @@ public class JarEntryRevisionTest extends XWorkTestCase { assertTrue(entry.needsReloading()); } + @Override + protected void tearDown() throws Exception { + Path tmpFile = Files.createTempFile("jar_cache", null); + Path tmpFolder = tmpFile.getParent(); + int count = FileUtils.listFiles(tmpFolder.toFile(), new WildcardFileFilter("jar_cache*"), + null).size(); + if (tmpFile.toFile().delete()) { + count--; + } + assertEquals(0, count); + + super.tearDown(); + } + /** * WW-4901 Simulating container implementation of {@link URL#openConnection()} From 0a3e2fa8a40de00322c6ebfc6359430eed8becce Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Mon, 10 Sep 2018 18:18:42 +0430 Subject: [PATCH 28/35] monitor file only if needed See also: WW-4948 (cherry picked from commit b816cc1) --- .../xwork2/util/fs/DefaultFileManager.java | 4 +- .../XmlConfigurationProviderTest.java | 56 +++++++++++++++++-- .../xwork2/util/DefaultFileManagerTest.java | 50 ++++++++++------- 3 files changed, 83 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/util/fs/DefaultFileManager.java b/core/src/main/java/com/opensymphony/xwork2/util/fs/DefaultFileManager.java index f5fe7e1df..21d6cc0ad 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/fs/DefaultFileManager.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/fs/DefaultFileManager.java @@ -70,7 +70,9 @@ public class DefaultFileManager implements FileManager { return null; } InputStream is = openFile(fileUrl); - monitorFile(fileUrl); + if (reloadingConfigs) { + monitorFile(fileUrl); + } return is; } diff --git a/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java index 252e1ba6a..b66c2ff57 100644 --- a/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java @@ -78,12 +78,14 @@ public class XmlConfigurationProviderTest extends ConfigurationTestBase { } public void testNeedsReload() throws Exception { - container.getInstance(FileManagerFactory.class).setReloadingConfigs("true"); final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-actions.xml"; - ConfigurationProvider provider = buildConfigurationProvider(filename); - container.getInstance(FileManagerFactory.class).setReloadingConfigs("true"); + ConfigurationProvider provider = new XmlConfigurationProvider(filename, true); + container.getInstance(FileManagerFactory.class).getFileManager().setReloadingConfigs(true); + container.inject(provider); + provider.init(configuration); + provider.loadPackages(); - assertTrue(!provider.needsReload()); // Revision exists and timestamp didn't change + assertFalse(provider.needsReload()); // Revision exists and timestamp didn't change File file = new File(getClass().getResource("/" + filename).toURI()); assertTrue("not exists: " + file.toString(), file.exists()); @@ -92,6 +94,24 @@ public class XmlConfigurationProviderTest extends ConfigurationTestBase { assertTrue(provider.needsReload()); } + public void testNeedsReloadNotReloadingConfigs() throws Exception { + final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-actions.xml"; + buildConfigurationProvider(filename); + ConfigurationProvider provider = new XmlConfigurationProvider(filename, true); + container.getInstance(FileManagerFactory.class).getFileManager().setReloadingConfigs(false); + container.inject(provider); + provider.init(configuration); + provider.loadPackages(); + + assertFalse(provider.needsReload()); // Revision exists and timestamp didn't change + + File file = new File(getClass().getResource("/" + filename).toURI()); + assertTrue("not exists: " + file.toString(), file.exists()); + changeFileTime(file); + + assertFalse(provider.needsReload()); + } + public void testInheritence() throws Exception { final String filename = "com/opensymphony/xwork2/config/providers/xwork-include-parent.xml"; ConfigurationProvider provider = buildConfigurationProvider(filename); @@ -155,10 +175,13 @@ public class XmlConfigurationProviderTest extends ConfigurationTestBase { public void testEmptySpaces() throws Exception { final String filename = "com/opensymphony/xwork2/config/providers/xwork- test.xml"; + ConfigurationProvider provider = new XmlConfigurationProvider(filename, true); container.getInstance(FileManagerFactory.class).getFileManager().setReloadingConfigs(true); + container.inject(provider); + provider.init(configuration); + provider.loadPackages(); - ConfigurationProvider provider = buildConfigurationProvider(filename); - assertTrue(!provider.needsReload()); + assertFalse(provider.needsReload()); URI uri = ClassLoaderUtil.getResource(filename, ConfigurationProvider.class).toURI(); @@ -170,6 +193,27 @@ public class XmlConfigurationProviderTest extends ConfigurationTestBase { assertTrue(provider.needsReload()); } + public void testEmptySpacesNotReloadingConfigs() throws Exception { + final String filename = "com/opensymphony/xwork2/config/providers/xwork- test.xml"; + buildConfigurationProvider(filename); + ConfigurationProvider provider = new XmlConfigurationProvider(filename, true); + container.getInstance(FileManagerFactory.class).getFileManager().setReloadingConfigs(false); + container.inject(provider); + provider.init(configuration); + provider.loadPackages(); + + assertFalse(provider.needsReload()); + + URI uri = ClassLoaderUtil.getResource(filename, ConfigurationProvider.class).toURI(); + + File file = new File(uri); + + assertTrue(file.exists()); + changeFileTime(file); + + assertFalse(provider.needsReload()); + } + public void testConfigsInJarFiles() throws Exception { container.getInstance(FileManagerFactory.class).getFileManager().setReloadingConfigs(true); testProvider("xwork-jar.xml"); diff --git a/core/src/test/java/com/opensymphony/xwork2/util/DefaultFileManagerTest.java b/core/src/test/java/com/opensymphony/xwork2/util/DefaultFileManagerTest.java index 32d24853e..c13e87383 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/DefaultFileManagerTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/DefaultFileManagerTest.java @@ -21,16 +21,9 @@ package com.opensymphony.xwork2.util; import com.opensymphony.xwork2.FileManager; import com.opensymphony.xwork2.FileManagerFactory; import com.opensymphony.xwork2.XWorkTestCase; -import com.opensymphony.xwork2.util.fs.DefaultFileManager; -import org.apache.struts2.util.fs.JBossFileManager; -import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; -import java.net.URLStreamHandlerFactory; +import java.net.*; /** * FileManager Tester. @@ -49,23 +42,40 @@ public class DefaultFileManagerTest extends XWorkTestCase { fileManager = container.getInstance(FileManagerFactory.class).getFileManager(); } - public void disabled_testGetFileInJar() throws Exception { - testLoadFile("xwork-jar.xml"); - testLoadFile("xwork - jar.xml"); - testLoadFile("xwork-zip.xml"); - testLoadFile("xwork - zip.xml"); - testLoadFile("xwork-jar2.xml"); - testLoadFile("xwork - jar2.xml"); - testLoadFile("xwork-zip2.xml"); - testLoadFile("xwork - zip2.xml"); + public void testGetFileInJar() throws Exception { + testLoadFile("xwork-jar.xml", false); + testLoadFile("xwork - jar.xml", false); + testLoadFile("xwork-zip.xml", false); + testLoadFile("xwork - zip.xml", false); + testLoadFile("xwork-jar2.xml", false); + testLoadFile("xwork - jar2.xml", false); + testLoadFile("xwork-zip2.xml", false); + testLoadFile("xwork - zip2.xml", false); + + testLoadFile("xwork-jar.xml", true); + testLoadFile("xwork - jar.xml", true); + testLoadFile("xwork-zip.xml", true); + testLoadFile("xwork - zip.xml", true); + testLoadFile("xwork-jar2.xml", true); + testLoadFile("xwork - jar2.xml", true); + testLoadFile("xwork-zip2.xml", true); + testLoadFile("xwork - zip2.xml", true); } - private void testLoadFile(String fileName) { - fileManager.setReloadingConfigs(true); + private void testLoadFile(String fileName, boolean reloadConfigs) throws Exception { + fileManager.setReloadingConfigs(reloadConfigs); URL url = ClassLoaderUtil.getResource(fileName, DefaultFileManagerTest.class); InputStream file = fileManager.loadFile(url); assertNotNull(file); - assertTrue(fileManager.fileNeedsReloading(fileName)); + file.close(); + assertFalse(fileManager.fileNeedsReloading(url.toString())); + + long now = System.currentTimeMillis(); + JarURLConnection conn = (JarURLConnection) url.openConnection(); + conn.getJarEntry().setTime(now + 60000); + conn.getInputStream().close(); + + assertEquals(reloadConfigs, fileManager.fileNeedsReloading(url.toString())); } public void testReloadingConfigs() throws Exception { From 3e29573a2e5ddf7b948ebe622a01d0fbc20a0b5b Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 18 Sep 2018 09:49:27 +0200 Subject: [PATCH 29/35] WW-4951 Upgrades to the latest struts master to support SHA256/512 --- assembly/pom.xml | 2 +- assembly/src/main/resources/docs-urls.txt | 12 ++++++------ pom.xml | 13 +++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/assembly/pom.xml b/assembly/pom.xml index 9db215a0c..4a72c940e 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -111,7 +111,7 @@ make-assembly package - attached + single diff --git a/assembly/src/main/resources/docs-urls.txt b/assembly/src/main/resources/docs-urls.txt index 845dab8cf..d96c3ea23 100644 --- a/assembly/src/main/resources/docs-urls.txt +++ b/assembly/src/main/resources/docs-urls.txt @@ -1,6 +1,6 @@ -http://struts.apache.org/getting-started/ -http://struts.apache.org/security/ -http://struts.apache.org/core-developers/ -http://struts.apache.org/tag-developers/ -http://struts.apache.org/maven-archetypes/ -http://struts.apache.org/plugins/ +https://struts.apache.org/getting-started/ +https://struts.apache.org/security/ +https://struts.apache.org/core-developers/ +https://struts.apache.org/tag-developers/ +https://struts.apache.org/maven-archetypes/ +https://struts.apache.org/plugins/ diff --git a/pom.xml b/pom.xml index 9af53c5cf..dbae089b1 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.struts struts-master - 11 + 12 4.0.0 @@ -279,11 +279,12 @@ true true false - - - org.apache.rat.analysis.license.ApacheSoftwareLicense20 - - + + + + + + pom.xml src/** From 6116287a5cbb16e8360a3ac768fed3b2a9963d1d Mon Sep 17 00:00:00 2001 From: Hang Shi Date: Mon, 24 Sep 2018 14:48:21 -0400 Subject: [PATCH 30/35] Fix typo in a local vairalbe naming --- .../xwork2/interceptor/PrefixMethodInvocationUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java index 011361431..040080824 100644 --- a/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/PrefixMethodInvocationUtil.java @@ -150,8 +150,8 @@ public class PrefixMethodInvocationUtil { public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) { assert(prefixes != null); String capitalizedMethodName = capitalizeMethodName(methodName); - for (String prefixe : prefixes) { - String prefixedMethodName = prefixe + capitalizedMethodName; + for (String prefix : prefixes) { + String prefixedMethodName = prefix + capitalizedMethodName; try { return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY); } From 633eec29b6a664c2ec84ab76e970c0433d8a9b22 Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Wed, 26 Sep 2018 12:48:33 +0330 Subject: [PATCH 31/35] fix false positive test pass Co-authored-by: Babak Vahdat --- .../java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java index 1bd07e5f3..c644bfed4 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilStrutsTest.java @@ -40,16 +40,19 @@ public class OgnlUtilStrutsTest extends StrutsInternalTestCase { try { ognlUtil.getExcludedClasses().clear(); + fail("Missing the expected Exception"); } catch (Exception ex) { assertTrue(ex instanceof UnsupportedOperationException); } try { ognlUtil.getExcludedPackageNames().clear(); + fail("Missing the expected Exception"); } catch (Exception ex) { assertTrue(ex instanceof UnsupportedOperationException); } try { ognlUtil.getExcludedPackageNamePatterns().clear(); + fail("Missing the expected Exception"); } catch (Exception ex) { assertTrue(ex instanceof UnsupportedOperationException); } From d123771433677eb8069ccc8e0ffac603eefa1957 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 27 Sep 2018 07:22:46 +0200 Subject: [PATCH 32/35] Fixes problem with building on Java 8 after upgrading to the latest apache parent pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dbae089b1..a91510fce 100644 --- a/pom.xml +++ b/pom.xml @@ -155,7 +155,7 @@ org.apache.maven.plugins maven-javadoc-plugin - -Xdoclint:none + none From b77b1c13ccc79987a12b8d0fd4bd82a8fd2f78ce Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 2 Oct 2018 09:00:00 +0200 Subject: [PATCH 33/35] Drops irrelevant profile --- core/pom.xml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 17d57ea02..c4a723cbf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -178,25 +178,6 @@ - - default-tools.jar - - - java.vendor - Sun Microsystems Inc. - - - - - com.sun - tools - 1.5.0 - system - ${java.home}/../lib/tools.jar - - - - From 13d9053050c9e4fb2ef049db6a37d3f6eebf48fa Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 4 Oct 2018 09:13:08 +0200 Subject: [PATCH 34/35] WW-4952 Uses struts parent 13 to fix issue with wrong release profile --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a91510fce..41f2fae96 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.struts struts-master - 12 + 13 4.0.0 From 53825df9fbf13dd8a0b30e8183486c6d8dacf371 Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Sat, 6 Oct 2018 17:31:27 +0330 Subject: [PATCH 35/35] increase test coverage --- .../opensymphony/xwork2/ChainResultTest.java | 37 ++++++- .../struts2/result/PostbackResultTest.java | 96 +++++++++++++++++++ .../ServletActionRedirectResultTest.java | 76 +++++++++++++-- .../result/StrutsResultSupportTest.java | 40 ++++++-- 4 files changed, 229 insertions(+), 20 deletions(-) create mode 100644 core/src/test/java/org/apache/struts2/result/PostbackResultTest.java diff --git a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java index 68def20c5..9c2208e44 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java @@ -72,6 +72,41 @@ public class ChainResultTest extends XWorkTestCase { } } + public void testWithNoNamespace() throws Exception { + ActionChainResult result = new ActionChainResult(); + result.setActionName("${actionName}"); + + String expectedActionName = "testActionName"; + String expectedNamespace = "${1-1}"; + Map values = new HashMap<>(); + values.put("actionName", expectedActionName); + + ValueStack stack = ActionContext.getContext().getValueStack(); + stack.push(values); + + Mock actionProxyMock = new Mock(ActionProxy.class); + actionProxyMock.expect("execute"); + actionProxyMock.expectAndReturn("getNamespace", expectedNamespace); + actionProxyMock.expectAndReturn("getActionName", expectedActionName); + actionProxyMock.expectAndReturn("getMethod", null); + + ActionProxy actionProxy = (ActionProxy) actionProxyMock.proxy(); + ActionProxyFactory testActionProxyFactory = new NamespaceActionNameTestActionProxyFactory(expectedNamespace, expectedActionName, actionProxy); + result.setActionProxyFactory(testActionProxyFactory); + + Mock invocationMock = new Mock(ActionInvocation.class); + invocationMock.matchAndReturn("getProxy", actionProxy); + try { + + ActionContext testContext = new ActionContext(stack.getContext()); + ActionContext.setContext(testContext); + result.execute((ActionInvocation) invocationMock.proxy()); + actionProxyMock.verify(); + } finally { + ActionContext.setContext(null); + } + } + public void testRecursiveChain() throws Exception { ActionProxy proxy = actionProxyFactory.createActionProxy("", "InfiniteRecursionChain", null, null); @@ -88,7 +123,7 @@ public class ChainResultTest extends XWorkTestCase { private String expectedActionName; private String expectedNamespace; - public NamespaceActionNameTestActionProxyFactory(String expectedNamespace, String expectedActionName, ActionProxy returnVal) { + NamespaceActionNameTestActionProxyFactory(String expectedNamespace, String expectedActionName, ActionProxy returnVal) { this.expectedNamespace = expectedNamespace; this.expectedActionName = expectedActionName; this.returnVal = returnVal; diff --git a/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java b/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java new file mode 100644 index 000000000..32c0cca31 --- /dev/null +++ b/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.ActionProxy; +import com.opensymphony.xwork2.util.ValueStack; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsInternalTestCase; +import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.easymock.IMocksControl; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import static org.easymock.EasyMock.createControl; +import static org.easymock.EasyMock.expect; + + +public class PostbackResultTest extends StrutsInternalTestCase { + + public void testWithNoNamespace() throws Exception { + + ActionContext context = ActionContext.getContext(); + ValueStack stack = context.getValueStack(); + MockHttpServletRequest req = new MockHttpServletRequest(); + MockHttpServletResponse res = new MockHttpServletResponse(); + context.put(ServletActionContext.HTTP_REQUEST, req); + context.put(ServletActionContext.HTTP_RESPONSE, res); + + PostbackResult result = new PostbackResult(); + result.setActionName("myAction${1-1}"); + result.setPrependServletContext(false); + + IMocksControl control = createControl(); + ActionProxy mockActionProxy = control.createMock(ActionProxy.class); + ActionInvocation mockInvocation = control.createMock(ActionInvocation.class); + expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes(); + expect(mockInvocation.getStack()).andReturn(stack).anyTimes(); + expect(mockInvocation.getProxy()).andReturn(mockActionProxy); + expect(mockActionProxy.getNamespace()).andReturn("${1-1}"); + + control.replay(); + result.setActionMapper(container.getInstance(ActionMapper.class)); + result.execute(mockInvocation); + assertEquals("
" + + "", res.getContentAsString()); + + control.verify(); + } + + public void testWithNamespace() throws Exception { + + ActionContext context = ActionContext.getContext(); + ValueStack stack = context.getValueStack(); + MockHttpServletRequest req = new MockHttpServletRequest(); + MockHttpServletResponse res = new MockHttpServletResponse(); + context.put(ServletActionContext.HTTP_REQUEST, req); + context.put(ServletActionContext.HTTP_RESPONSE, res); + + PostbackResult result = new PostbackResult(); + result.setActionName("myAction${1-1}"); + result.setNamespace("myNamespace${1-1}"); + result.setPrependServletContext(false); + + IMocksControl control = createControl(); + ActionInvocation mockInvocation = control.createMock(ActionInvocation.class); + expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes(); + expect(mockInvocation.getStack()).andReturn(stack).anyTimes(); + + control.replay(); + result.setActionMapper(container.getInstance(ActionMapper.class)); + result.execute(mockInvocation); + assertEquals("" + + "", res.getContentAsString()); + + control.verify(); + } + +} diff --git a/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java index e0c955b40..365fb945a 100644 --- a/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java @@ -28,7 +28,6 @@ import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsInternalTestCase; import org.apache.struts2.dispatcher.mapper.ActionMapper; -import org.apache.struts2.result.ServletActionRedirectResult; import org.apache.struts2.views.util.DefaultUrlHelper; import org.easymock.IMocksControl; import org.springframework.mock.web.MockHttpServletRequest; @@ -41,9 +40,6 @@ import static org.easymock.EasyMock.createControl; import static org.easymock.EasyMock.expect; -/** - * @version $Date$ $Id$ - */ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { public void testIncludeParameterInResultWithConditionParseOn() throws Exception { @@ -76,15 +72,15 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { context.put(ServletActionContext.HTTP_RESPONSE, res); - Map results= new HashMap(); + Map results= new HashMap<>(); results.put("myResult", resultConfig); ActionConfig actionConfig = new ActionConfig.Builder("", "", "") .addResultConfigs(results).build(); ServletActionRedirectResult result = new ServletActionRedirectResult(); - result.setActionName("myAction"); - result.setNamespace("/myNamespace"); + result.setActionName("myAction${1-1}"); + result.setNamespace("/myNamespace${1-1}"); result.setParse(true); result.setEncode(false); result.setPrependServletContext(false); @@ -103,7 +99,69 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { control.replay(); result.setActionMapper(container.getInstance(ActionMapper.class)); result.execute(mockInvocation); - assertEquals("/myNamespace/myAction.action?param1=value+1¶m2=value+2¶m3=value+3#fragment", res.getRedirectedUrl()); + assertEquals("/myNamespace0/myAction0.action?param1=value+1¶m2=value+2¶m3=value+3#fragment", res.getRedirectedUrl()); + + control.verify(); + } + + public void testIncludeParameterInResultWithConditionParseOnWithNoNamespace() throws Exception { + + ResultConfig resultConfig = new ResultConfig.Builder("", "") + .addParam("actionName", "someActionName") + .addParam("namespace", "someNamespace") + .addParam("encode", "true") + .addParam("parse", "true") + .addParam("location", "someLocation") + .addParam("prependServletContext", "true") + .addParam("method", "someMethod") + .addParam("statusCode", "333") + .addParam("param1", "${#value1}") + .addParam("param2", "${#value2}") + .addParam("param3", "${#value3}") + .addParam("anchor", "${#fragment}") + .build(); + + + + ActionContext context = ActionContext.getContext(); + ValueStack stack = context.getValueStack(); + context.getContextMap().put("value1", "value 1"); + context.getContextMap().put("value2", "value 2"); + context.getContextMap().put("value3", "value 3"); + MockHttpServletRequest req = new MockHttpServletRequest(); + MockHttpServletResponse res = new MockHttpServletResponse(); + context.put(ServletActionContext.HTTP_REQUEST, req); + context.put(ServletActionContext.HTTP_RESPONSE, res); + + + Map results= new HashMap<>(); + results.put("myResult", resultConfig); + + ActionConfig actionConfig = new ActionConfig.Builder("", "", "") + .addResultConfigs(results).build(); + + ServletActionRedirectResult result = new ServletActionRedirectResult(); + result.setActionName("myAction${1-1}"); + result.setParse(true); + result.setEncode(false); + result.setPrependServletContext(false); + result.setAnchor("fragment"); + result.setUrlHelper(new DefaultUrlHelper()); + + IMocksControl control = createControl(); + ActionProxy mockActionProxy = control.createMock(ActionProxy.class); + ActionInvocation mockInvocation = control.createMock(ActionInvocation.class); + expect(mockInvocation.getProxy()).andReturn(mockActionProxy).times(2); + expect(mockInvocation.getResultCode()).andReturn("myResult"); + expect(mockActionProxy.getConfig()).andReturn(actionConfig); + expect(mockActionProxy.getNamespace()).andReturn("${1-1}"); + expect(mockInvocation.getInvocationContext()).andReturn(context); + expect(mockInvocation.getStack()).andReturn(stack).anyTimes(); + + control.replay(); + result.setActionMapper(container.getInstance(ActionMapper.class)); + result.execute(mockInvocation); + assertEquals("/${1-1}/myAction0.action?param1=value+1¶m2=value+2¶m3=value+3#fragment", res.getRedirectedUrl()); control.verify(); } @@ -131,7 +189,7 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { context.put(ServletActionContext.HTTP_RESPONSE, res); - Map results= new HashMap(); + Map results= new HashMap<>(); results.put("myResult", resultConfig); ActionConfig actionConfig = new ActionConfig.Builder("", "", "") diff --git a/core/src/test/java/org/apache/struts2/result/StrutsResultSupportTest.java b/core/src/test/java/org/apache/struts2/result/StrutsResultSupportTest.java index fcddf6fa0..26929df4a 100644 --- a/core/src/test/java/org/apache/struts2/result/StrutsResultSupportTest.java +++ b/core/src/test/java/org/apache/struts2/result/StrutsResultSupportTest.java @@ -18,17 +18,16 @@ */ package org.apache.struts2.result; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.struts2.StrutsInternalTestCase; -import org.apache.struts2.result.StrutsResultSupport; -import org.easymock.EasyMock; - import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.util.ValueStack; +import org.apache.struts2.StrutsInternalTestCase; +import org.easymock.EasyMock; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * Test case for StrutsResultSupport. @@ -61,6 +60,27 @@ public class StrutsResultSupportTest extends StrutsInternalTestCase { EasyMock.verify(mockActionInvocation); } + public void testParseButNotParseLocation() throws Exception { + ValueStack stack = ActionContext.getContext().getValueStack(); + + ActionInvocation mockActionInvocation = EasyMock.createNiceMock(ActionInvocation.class); + mockActionInvocation.getStack(); + EasyMock.expectLastCall().andReturn(stack).anyTimes(); + EasyMock.replay(mockActionInvocation); + + InternalStrutsResultSupport result = new InternalStrutsResultSupport(); + result.setParse(true); + result.setEncode(false); + result.parseLocation = false; + result.setLocation("${1-1}"); + + result.execute(mockActionInvocation); + + assertNotNull(result.getInternalLocation()); + assertEquals("${1-1}", result.getInternalLocation()); + EasyMock.verify(mockActionInvocation); + } + public void testParseAndEncode() throws Exception { ValueStack stack = ActionContext.getContext().getValueStack(); stack.push(new ActionSupport() { @@ -110,7 +130,7 @@ public class StrutsResultSupportTest extends StrutsInternalTestCase { EasyMock.verify(mockActionInvocation); } - public void testConditionalParseCollection() throws Exception { + public void testConditionalParseCollection() { ValueStack stack = ActionContext.getContext().getValueStack(); stack.push(new ActionSupport() { public List getList() { @@ -142,11 +162,11 @@ public class StrutsResultSupportTest extends StrutsInternalTestCase { public static class InternalStrutsResultSupport extends StrutsResultSupport { private String _internalLocation = null; - protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { + protected void doExecute(String finalLocation, ActionInvocation invocation) { _internalLocation = finalLocation; } - public String getInternalLocation() { + String getInternalLocation() { return _internalLocation; } }