jenkins-remoting-2.23/0000755000175000017500000000000012202116444015371 5ustar jamespagejamespagejenkins-remoting-2.23/assembly-uberjar-14.xml0000644000175000017500000000364312122627370021620 0ustar jamespagejamespage jdk14-jar-with-dependencies jar false true runtime target/classes14 jenkins-remoting-2.23/.gitignore0000644000175000017500000000007212122627370017366 0ustar jamespagejamespage*.iml *.ipr *.iws target /.classpath /.project /.settings jenkins-remoting-2.23/pom.xml0000644000175000017500000002416612122627370016725 0ustar jamespagejamespage 4.0.0 org.jenkins-ci jenkins 1.29 org.jenkins-ci.main remoting 2.23 Jenkins remoting layer Contains the bootstrap code to bridge separate JVMs into a single semi-shared space. Reusable outside Jenkins. The MIT license http://www.opensource.org/licenses/mit-license.php repo scm:git:git://github.com/jenkinsci/remoting.git scm:git:ssh://git@github.com/jenkinsci/remoting.git https://github.com/jenkinsci/remoting remoting-2.23 private UTF-8 UTF-8 UTF-8 repo.jenkins-ci.org http://repo.jenkins-ci.org/public/ true false junit junit 4.11 test asm asm-all 2.2.3 test args4j args4j 2.0.16 provided commons-io commons-io 1.4 test org.jvnet.hudson test-annotations 1.0 test org.jvnet animal-sniffer-annotation 1.0 true com.github.stephenc.findbugs findbugs-annotations 1.3.9-1 provided ${basedir}/src/filter/resources true ${basedir}/src/main/resources org.codehaus.mojo findbugs-maven-plugin High org.apache.maven.plugins maven-pmd-plugin 1.5 maven-jar-plugin hudson.remoting.Launcher ${project.version} maven-jarsigner-plugin 1.2 ${hudson.sign.alias} ${hudson.sign.storepass} ${hudson.sign.keystore} sign maven-dependency-plugin bundle-arg4j process-classes unpack-dependencies ${project.build.outputDirectory} provided args4j args4j org.codehaus.mojo build-helper-maven-plugin timestamp-property timestamp-property now MM/dd/yyyy HH:mm z en_US version-property regex-property build.version ${project.version} -SNAPSHOT -SNAPSHOT (${build.type}-${now}-${user.name}) false org.codehaus.mojo animal-sniffer-maven-plugin 1.7 org.codehaus.mojo.signature java15 1.0 maven-release-plugin 2.3.2 release rc rc org.codehaus.mojo build-helper-maven-plugin version-property -RC (${now}) debug true jenkins ${basedir}/src/test/keystore/dummy.keystore jenkins release maven-gpg-plugin sign-artifacts verify sign jenkins-remoting-2.23/logo.xcf0000644000175000017500000024602712122627370017054 0ustar jamespagejamespagegimp xcf file BB  icc-profile H HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmgimp-image-grid(style intersections) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) {]I overlay      Wco '7GWgw'7G}>C! Logotype      (DEQ*I0:IV?_:pyP*| "; "#:!##: "##9 "##8##7"##7#6"##5 ##94"##,3 "##%3#2"##g3"##.2"#2!##B2#ÃE0"##If;##1"# #0" #.#"# #,""##+"##*##L("##I(#0(" #$k'"# #G'"# #e%# #v٬x%!# #$xmm$# #&Ցmm%"##+ymm;:998776554(3!32d3*22>2ٿA0Ec71 0 . ,+*I(F(,(  g' C' a% r֪v%  vkk$ "Ҏkk%'wkk ; ;  :   9   8  7  7 6  5   64  )3   "3 2  d3  +2  2  ?2  ؿB0  Fc8  1 0 . ,  +  *  I(  F( -( !g' C' b% rժx% !wmm$ #яmm% (ymm;:/9(98 776L55~4 43D 3 2 2c 2 1 1w 04 . ,j+*))(4''&#&%%q|"#5 !"#"##3!"#"##/""# #,""#)!""##'"#$"##!""#"##""##""##%\!#<!"##1#"##* "##%s# #)\-# #a# #+`##P "!# #O[##A !# #,nj##& #"# #AV%##H "# #F~##%"# #Fb##D"# #F׳=##%# #8ץsa/##A!"##'yتumR&##$}#"##Qڮxmj@##> ##4۴{mmi4##d"#bmmf1###*Ԓmmj.##)#Dxmm@##6 #p؞nmmW##I #*ǀmmg)##L #0۩omm=##L#7ڒmm[##L #A։mml0##L #LтmmM##K K{mmf'##4 umm9##% tmmN## zmma%##j ׁmmk0##F یmmk:##$ mma5##o PD8-/6<>>=7$##) C## #1 # # #> w?##)DcX##X##2s##W##(_##I-## 4##d## V##\##omQ##Y##ٺtmmZ##W# #ٷrmm^##T##*dym mc##lR##Km mg##jq##. ml##r##`m&##y4##wm$####|53/ , )'$!!T6,%z!i %X) Y '\I  KW;  (jf"  =R!A  Bz!| B_>B԰:!w4ӣq_+;#vէtkO" r N׬vkh=81رzkkg1[^~kkd-{&ҏkkg+$@vkk=1 l՚lkkTC &~kke&E ,ئmkk:E3אkkXE =Ӈkkj,E HkkJD Gxkkd#/ skk5! rkkK xkk^!a Ԁkki-? ؊kki6 kk_1e MA4),28;::4 $ ?z ,  v 8 m9%A_}TP.oO$\C( 1[ ST׽~mkNQַrkkWO ִpkk\yM%Ywk kacJCk keah) kjhVk"o.jk vy{|5    3   / ,  )  '  $  !        !O  4}   ,  %p  !a &X* S (]  E  KX  8  )jg  #u  >S"  >  Cz  "q C`  ; Cӱ<  !m 5ӣsa,  8  $vԨumQ#  !iƾ  N֬xmj>  6¹ 1ױ{mmh2  U  _mmf.  qø 'ѐmmi,  %Ĺ @xmm>  0Ǻ  lԜnmmV  ?  'mmg'  Aɻ  -קomm;  A· 4֑mmZ  A ʼ >҈mml-  A ɹ H΁mmK  A ƸHzmmf% . ķumm7 " tmmL  ~ ymm`" Z Ӂmmk. < ׋mmk8 ! mma2  ^ ·OB6*-3:<<;5!  % ķǼ@  p ȸ + ˹ |  m  6 Ʒe7  &A_}U  ˹K  /o  vJ  %\  ?ŷ(  1  ˹U  S  N  ֽomO  ɷL  շtmmY  J մrmm^  oǷH  %Pxm mb  \F  >m mg  Zŷ`  (v ml  `  Mm#  gķ.  ^m!  m̹o  l|Jv5@l3.y/ K ,i ) \'<$|![ o x!#v%W'9)!+, k. I0 $2 3[57\8:P;~,""!#""#""#!" "!"#"#"!#!$#" '#"##"-#" /#"! 2#"! 4#"7#"#+Mjopsuxz|~t^G0# #"!#/Rt m@##,q绎a4##"!ܮo2##"%ÃB##*֖U%##-܉9# #/`%# #2ދ9##4?## =##;## .##*P#3:;986ݫ6#8y5#/4 #3 #'2 #s2 #42 #=2#->PB(##{2ֹs !%'+ / 247'FaegiknprtkV@+ +Kk Dzd:'hլY/!ˠf-&x<*ŊM!-~3 /W! 2̀449 75 ߑ)*I3{:;976̝62o5*4 x3 #2 f2 .2 62(7G;#n2տs  !  &  ' +  /   2  4 8  'BZ^_acegikbP=+ !  *Gb Ƴ|\8  '`xS.  !^,  %ˣn9  *~I!  -s2 /ʗR! 2u2  4ʋ7   ʉ5   ʆ4  ÿ󸻿Ȅ) *̶E 3p:ü;÷9Ǹ7ȹ6Ƿ6 1g5 *4 i3 #2 [2 ,2 32 '4A7$  a2zs!2CUfxļzriO- !ǥ`>#АO'̋J +džD /* 2]4)7[9E;;p=  ="#;# :#:#"7#"6#" 5#!4 #!3 #"2 #"1%# # 07# #0_# #".# #!-$##"#,$##" +$# #+# #)# #"(# #"' x# #"' Q# #"& 5##"& &##"% # #"# j# #"# H# #"/##" !$##"!# #!  _##" >##" *##"밈aLMOY##"ހ.##.##"$##%##"C####"## b##&Qɴ{B##"h#V׊0# # ~ 1# #i# #",##">##!;##"8##)##" ## =;:976543 2 1! 01 /U . . , +  * ) ( ' k ( H & 0% "$  $ ^ # A "+! !  W 8 &آ}XEFHQwv)) !=Y"Hvûn;]M{* p |+ ] '752$~ =; : : 7 6 6 4 3 2 1! 0/ /N /v -!   ,|!  +z! *w )u (r ' _ ' B & .  & "  $ u # T # > "* "!  !u    Q   6   &  ÓsRACDLmu  l)  )  !  "  :  ͣ  t  R "Bha7  S Fl)  ĸc| m* T }'  4  2  /  $   o  8=;?:`97 65.4F3 J2 L1 O0 Q/ S.V-X,W+;*#)(''w&S%5$##A"!!/ rK | !H!!"v""#=#~##$@$"##nmm$##fmm$"##9mm$ #mm#"##>m m# #sm m##*ۈm m##cm m#"##rm m"##m m"#2tm m""##[m m""##s~m m""##mm"!##ܦmm"#܄mm"#qmm"#mm"#ܿmm""##ܯmm""##ܟmm""##܏mm""##mm""##umm"#ymm""##}mm"#܀mm"#܄mm"#܈mm"#܍mm"!##|ܟmmhbhmm##nܴmmaLA5)##%+2$# #####1uw-##"##c##12##"##,####!####/M# ####g#####a######"##t##"##F##$lkk$ckk$6kk$kk#:k k#qk k#&؅k k#_k k#pk k" k k".qk k"Wk k"o|k k"kk"٤kk"قkk"okk"kk"ٽkk"٭kk"ٝkk"ٍkk"}kk"skk"wkk"{kk"~kk"قkk"نkk"يkk"yٜkkf_fkk#kٱkk_J>1%!'.9Rik"\Y6 #<"LF###""$==1! PǕH [ 2 D  h pPn/1\"#er{ 5+  xk< 6cF%)2`!E f8  ԭ+hj(X+-'*D[~Vk?$  nmm$  cmm$  6mm$  mm#  ;m m# rm m#  'ׇm m#  _m m# rm m"  m m"  .sm m"  Xm m"  o~m m" mm" ؤmm" ؃mm" qmm" mm" ؼmm" حmm" ؝mm" ؎mm" ~mm" umm" xmm"  |mm"  mm" ؃mm"  ؇mm"  ،mm"  y؝mmhahmm#  kرmmaK?3&  "(/:Tkm"  ]Z7 $="  MG  #  #  #  "  "  %::0  ! KyD  U ̎1 A   `  ! ~  gȿ  Kc. /Sz #x#  Z  io 2 * !i  c9    3X@%  (  0Y"v?   ^6!   x|  *\y^'    ·O  *+   '  {     )?  ʷ  R   ŷ  nN        b    <  $$A$$##X###""B"j"""""""""""""""""n"B"######"#"!F  NCx +124560 mh##.## mc####l m]##H##I mX####. mS##'## mJ##D##: m?##fc## m5##f##H m*#### mj##$%#Z m_##D> mU##p mK##+ mA##+ m7##*+ m-##Q, ml$##}, mc##, mX##, mN##', mD##F- m:##j- m0##- m&##- m$##- ml##- mk##- mj##$- mh##+. mg##$. ml$##- m-##-Ylmm6##-#1_mm@##-#$HlmmJ##-#0ammT##p-#)cmmc##A-#-fmm-##,#1immA##,#:mmU##,#^mmh$##N,@##;mm2##%+A##$ammF##+&##?mmZ##+##,mmh##O+2##$kmmj##&*##amma##*%##VmmC##*c##FT=##+# #p-;# #V*o# #;*J# #I*## #S*#:9##-v+#k##(>g-#1#F;#:#:#:Z##:%#;#: kf}) ka` k[BA kV) kP# kG>y3 k<]Z k1^@ k& kh !Q k]=8 kSe kH+ k>+ k3%+ k)H, kj o, k`, kV, kK#, kA?- k7^- k,~- k#- k - kj- ki- kg- kf&. ke . kj - k)-Vjkk3-.]kk=- EjkkG--^kkQd-&akka:-)dkk),.gkk=,7kkRq,\kkf E,:8kk.!+; _kkC+";kkXt+(kkfF+- ikkh"*y_kk^*!Tkk?*ZBR:|+ d-5 L*d 5*B A*v J*42(i+`#7[-xì1?;:::Q: ;: mh  s·)  r mb  ̹z  V m]  ŷ?  < mW  ͷ  ( mR  #Ʒ͊  o mH  ;o  1 m=  VT  ~ m2  uW  ; m(  {  mj  !" J m_  :6 mT  [ mJ  y+ m?  + m5  %+ m*  C, mk!  b, mb  , mX  , mM  #, mB  :- m8  T- m-  o- m$  - m!  - ml  - mk  - mi  - mh  &. mg  !. ml!  - m*  -Xlmm4  - /_mm>  - "FkmmH  z- .`mmS  Y- 'bmmb  6- *fmm*  , /imm?  , 8mmT  d, ^mmh! ?,8  9mm0 "+8  !ammE  +ͩ#  =mmY  f+u  )mmh  A+,  !kmmj  "*o  amm`  *!  UmmA  *T  DS;  l+͞ Y-4 E*Z 2*=} <* h D* 10  (]+ U  $4R- i1 :; : : :I :! ;w :== $##a;## c##l##5 ##+## X##?##- ##^2##y O##[o##' ##R##im##fG##$,####;##D,##l+##}##IiA##.##0I##$;[|E####W##8####.##$##&V##_##0##_##)##=##Yl## qG&##-G`<## ó$%#<#~N2) "LT*T$Ӂ6O` ԟd@"(@W5 Ϯ$ =j¸98 K##"##">####"##!D##"##"## 5##  W##" x##" ##+##*##21##";#P##C#=a## V##l##!##Ix##! ####" $##W##" G####" ##e##" ##$## U##`## #### /##\##" x####" ##K$## I##.##q##/:##!ϯl,##kF##" ե|l^O@+# #R##! 7##RX## '##/[## K##7^##"u##Za## 7# #9d##(3##>g###F҇H##'Tj##!#k m###p###%s###/t###Bk###_a##"#V##"+##K##"##)A##"##15##c##=+##"F##K##"2##]##&##r## ####!u####!^##|##"N##l {##"B##i a##.## G##" ## -##" u## ##" ;## ##! ## ##C7u<| 0 N kյ '&z-+4G<6VN`{@k v  N @ yZ   LW  *R j C  A)d*4˹`(_? n`SF9& I 2IN "*Q C2TiPV2 3Y#.8\?x@"K}^_ ¹ ad g*g;`UVuM&C%:w,0X6&?C-S"e{h~ToF` n:] V)x ?  ( hy  5w  yv >    4  g    8  l     .   F   _   x  '  %l  ,*  2 wA  : 3N  I V  q <_     h   ! Gq   <  y   o  Q     !   E  M       ƴ)  J   ĺ^     ž  >!   <  w(  Z  )1  V'  U:   aVK@5& C   0  CG   #  )I   >  0L  \  HN  r0 0wP  $+  4|R   :i;  #DnT   U V   qY   ![   )\   7U   LN   gF  &  >    %6  i  +.  O  3&  :  >  +  K  "  Z  x  l  \  n  L  bt  @  V a  7  T N  (  i ;    (   \  j   2  i   j  h  $$$ %C%%%%& &D&h&&&&''?'c''''''''''( (('(5(>(9(3(-((("(((( ((''''''''x'`'G'/'&&&&&&x&N&$"##$'## ##J####s##"##>##!##$#!##pd#"##+' "## #8 "##!"##-!!##z""##&"#8""##S#"##y#"##$!##v %"##X %# #B &!# #W&!# #Fn/'!##$(##("##)##+"#, ##-"##/!# #0#"# #2""#5 "#"##9#9#9"##:"##:"##:#:"##:"##:"##;!##;"##<#<#<"#<#="#="=  #B~g8 gY'#  2 !(!p"""2"L#o#$j % N % : & N& ?џb*' #()+,-/ 0 25999::::::;;<;;<<== !ʷ#   Ʒ=   s÷[   6z  !   _P   '#   v  1÷   xɷ!  (͸!  gͽ"  #ķ" 1"  G#  f#  x$  ^ %  G % 7 & F& :yW)' !$  (  )  +   , - / 0 2   5   9  9  9  :  :  :  : :  :  ;  ;  < ; ; < = == y? T   X !!>""X"#j#$>%%&='d(()+p,H-)/M 0_ 2a58~9_939 ::::a:5:;;%<;f;<<<==<ԥ75##E5N##3*##2m##>1#1#k1#C1#C1#@1#+2#0#$0#0#0#0#1#/#[/#0#{###\%2#(Q#V####w##%#9J##:###c#I###?##)#H##C#o##o#*Z##"##4##%!##N$##1 "##}##D "##~J##_ "##-,##{ !"##%## "##2## "####$##9h##%"##S##("##AF##&"##F##"##9E##{ #D##h "# #'C##Ur<7~0?5F3%2a811b1=1<1:1'20 00000{/S/+nT!-$HLz|m!4A4XA9$@<fc&Q.!G + o< tBU ('m !y - r 3]!J#;?">4=m y<] #<Kd<·7n. <ķ5@  ·3}%  ˷2V  6ͼ1 1 [÷1 :ŷ1 :ȷ1 8˷1 '2 z0 !0 0 ķ0 ȷ0 ̷0 q/ M/ +· Ƿa ̸  N"+ $·B E Ƿk l e̸ " 2< 1 ·  O xɷ<  x 7  $ ÷;  8 ^ʷ  X &I  u ķ,  "  C˷!  *   b  8   kŷ=  L   (̺'  a   wķ"  k   ,x  t   ķe  !~  2S  " ķD  $ 8:  "~  ķ9  o 29  a oƷ8  S  #8  E @=m=<8;;::L:9999q:|;<)555446657#@9B##9$##9##{9##0q###ޞZ$##)##- ֕S%##%^##,ŒJ##%##/;FRD*# #%$##&p##%S%E##*W(+# #%Fr͵v;4#V,##I%L##3TtI# #+Kkzm6Q# #A*# X##(## *####a####xO=D####f##5m*###### M$######0 ޫwB##$#u##TܨgH*# #/0k##ع|naW[]acf]##n-7##X8%##E8##76P##75.##14##$Ey1w# #Ev*`##(>T\biov||wrnje`\XSOJOP#%#C###@c#!#1(##JS##0x|1##)X"ʠuL:1&##)>Tj.º)555446~6/}7:8:9 9n9*e#xōP #$( J %S'ŭ|B%*5?I<% % &d J%=%N(& !?e򷡚}yh;.M+@v%D.KhA &C_mv`/H :& N# z%WֳkF7<ֶ[.թa& ˋE r* ǘi: iKÖw\@& *_rznbVNQRVX[Rb-2N8!>8v06G05)+y4 >l1i =h*W#8KRW]chnsrnifb^ZWRNJFBEG%;#9X!+#BJ*ko+$N"ӵiD3+"$8K^r.ǿ)̺55Ʒ54ɻ4ĸ5n˾6-qɺ7y 7ȸ87 8! 9  a9  )Y#  i{H! #$  ' tD!  %K  'qm=  %z  )2:B8% %!  &Y  !Du%9  %Fx(& ":Zz򞋅ysntj];, F{+|  U`iqyV.ɻB 6% ɸ G  # ̺ l%  ķN  _@4s8  ɷR  -V%  | ķ|  t y>!  y e  ) ^6  w! \  DiR;% s)ͽU  dﶥvlaWMGIKMOQͽK  W-0  G8!  :8h  .y6A  .r5(  *j4  !:`1^ 9]s*M  #4DJOSX]aeea^[WTQMJGC@=@A % n7 # 5O ! *v#  =C  )_b*  $Gn"|\?1*"  $4DTdu. ## ##" ##& n##" ##U D##" ## %## d##% ##" %##V ## q## k##n%## B##[##? $##"J##) ##";##Z ##!܃0##7b##(##%{-###U##"#[###$cD###%kr|##'r{(2##"+##b##".##K,## 2##$## 7##nw## ڈ8##/0##"b&####Ց># #|Z##ڰ[2# #O%###=##!#7>##"#3##" #=o##" #_,## #%##" #_ 2##!#S ###F 6## H`w## :##" ##! ?##" ##"!4##!t###$##""G##!###"# ,## $c##"%##"& 6##"& h##"#& ##( 1##"' `## ' ##' .##" 'X# #(# #!&# #"&+# #"$;##!#:##"#/##""'##! d# #(>'# #"   " b K <   Y  M  ez `b! :Q8 ӕA$y ͅ5P t*1W#!m(KQr X<!_fn#em#-u&Wz)C' ς-  ҈0cj y1**գW"8 oQvQ, F 6|27. 6c T' !|} T -J ?w 0@Ui4  8 !.!g" "?## '$X%% 0' ]& ' ,' V' ' )'N 'x & %& $5#4"*!# Y )['   r    " W    E 8   x  t !   P  !   !  F s   Y  k U  W"   7  I  5 !  <  $j  t2  H r  f)  /N  $  "a'   E   Id   !O8   "U[a  #Z`#,y  f& zN  l(  >'   r,  !   w.  W^   j/  ))  O"    q4 bI  iI+ @!   3m   04   +   3X   L'   !mn   L +   C r   :i .  ;M^vx  1  !|   5   ~  !,  !\  "!  ";  #r  # '  %O  %  % .  & S  &  ( +  ' M  ( }  ' (   'G (i & %& %2  #1  ")  !#   P -m*  %%%%}%S%)$$$$$U$###`#""""i"+!!!q!1   M he 7 m  !$!""e##@$%%%&,&'R'(''''^&%!$'#-"4!F h>!;##9!"##7""##7"##5"##3# #1#"# #0!"# #.!""# #,!##&* "# #'8G("# #):HII&"# #)=II&!# #%7HII$ "# #0EII##"# #*@II!!"# #%9I I!# #2FI I!"# #+AI I"# #&;II # #1GII# #&=II" #.FII "# #%:II!"# #,DII""# #$7II!"# #*BIIHA7 !"##4HIIG>533 ##'?IIF<433 "#/GIIC:33 ##$8IIA733 "# #(AIIH?53 3 # #/FIIF<43 3 # #$8IID:33 "# #(AIIB833 !# #.FIIH?633 #$8IIF=433 "##'AI ID;33 !##*I IB833 ##@I ID33 "##0I IH33 "##$EI I833 "##6I I=33 ##'HI IB33 #33"##6IIC33"##)IIH33 #BII733"##5II;33"##  #%"##"&"##"&##"&"##"&"##"&>;97753 1 0 . ,%*(Mn( ,Rprr& +Wqrr& #Kprr$; 9 7   7 5  3  1 0 . ,  '* +[(  0a& 0h& %Y$  E#  3q! '^  !J  7u  )b  I )j @  %a  9}  #Y 2w{h   Pveaa   -prbaa   Bnaa  "\|iaa  -twea a Asca a  "[oaa -t}jaa  @xfaa   "[tcaa  ,t obaa   4 ~kaa   r aa  E baa   " jaa   V taa   + }aa   g aa : gaa   x paa K zaa   %aa   \ caa 2 maa  ywaa  Uaa  0aa  whaa  Rqaa  ==5**  %  &  &  &  &  &>T;9 7a7A5&31` 06 . ,s+H)#' &T$#`!l'y0 !J#0%&( z*k, . %/ 0 1 1 92 x2 {2 v2 q2 N2 2 1 1 b1 $1 0 0 ,0 / / ./.u.--_- ,,I,++2+*r*"# #ZB##2##B##w##9B##pA##B##c##'%##.?3# #6##$1BII=&# #S#$3DIIA%# #/5FII?$# #2I I;# #/tu;## I8##7~s&## I4##-T} B##I0##(Jl\# #IH,##.@I# #IF)##;N##ID&##r)##I<$##ܻwV?*##NI IH@63.##7 ŰoY?## IG=433,##]##r IE;33/%##G ##7IB93 32*##0 ##IH@63 30&##AV##IG>533,$##?,##5IE;4331)##&\##YC9330'##5z##i33.&##+Qy|##z33-%# #'##!32,%##$3-%# #&3-&# #(3.&# #*3.&##,3/'##.3/(##03'##03*##03-##030##13$##}13'##`13*##B13-##'131##23&##23*##]23/##)232$##33(##p33,##.331##43%##X43*##$43.##53$##=53(##5##j8#.8##8 v9-:P:##R;-:j4:g::X#Ձ!7]A v0 =crrY% L"Bhrra# *Gkrr[! -r rT +k̜h5 rM2sf" rD(Mr :r;#B`rR rp3)9A rl,8Krg&o%rX!ܺtS;&%4676/r rpcUPA 3 ĮlV;1== rn`RPP<Z+== rk\QPPC&C ";==rhYP PN5 , -==rqdUP PG)=S6==roaSPPO:!;("==rl]QPPJ0"Y'==iYPPF+1w)==<;PPA''Mvy,==:88PO<$ #.==988!PN;$1==<88#PO<% 3==;88%PO>' 6==:88(P@( 7==:88*PA*6==988,PC+5==988.PE-4==:880P+4==:880P53==:880P>2==;880PH0==;880PO!,==;881P*(==;881P4$==<881P= ==<881PJ9==82P'0==82P5'==9882PB <==9882PN!3==9883P-*==:883P;!<=:883PI2=;884P%'=<884P39<884PB.<884PO!#<885P/5885# )888!8883888 +:889:88:&32: M̺7  +  ȷ7  ^  2·7  _6  uʸ7  ŷO  #q"  ?oL l̼.  "Fxj( G̻ $M~u% )v˺Tn" ,|ʹ e *bʺ\2  [  0j̼["  P  (Hi 7  D  $=Vdq~J 9  (5< 0  8L  })  p&  j#  ܺuT<'  &2454. ygaM!  4 įmW<  /:: tdaaF  Z  *:: pbaaP)  C  #8::~la a_>!  -  ,::zga aU-  >T  4::udaa`C#  ;)  "::qbaaY7  #Y  '::maaT0  2w  )::87aaN+  (Mvy  +::7433aa`G& $  -::533!a^E'  /::933#a_G( 1::733%a`I* 4::633'a`K, 5::533*aN.  4::533,aP0  3::533.aR3  3::5330a0  2::5330a=  1::6330aJ  0::6330aV  /::6330a`#  ,::7331a/  (::8331a;  $::9331aH  !::9331aY  6::32a*  /::4332a=  '::32aO  !9::4332a_"  2::5333a2  *::5333aE  !9:6333aW  1:7334a(  ':8334a;  78334aN  -9334a_#  #9335a5  2335*  (338 !4338 1338 *5339 633:&/.:  >;?D8b##)4)##D2>##V.^# #$k)P##)|%># #X##$Zs/# #F##5x5##:##$\ԥv9##5Xt@# #>n d5#,#1IT^itvbN9&# #*Eq3#3#")O(##b`/##O(# #"4d"##Mǔ\'##$qH7*&- \##j{-# #%i4##98# #"*Bg|&##*G##g>##* (## W##s X##/iS##2͒lE%##*{##q####i##2# #1n/##j##&]٣m+####_E##{##݉5# #^##ӵy=##2##m*##6##.##q##!9##m&##)1##1oJ##(*##'$##'Js$##(Hlg# #4T\'# #*KeI##&g΋9# #4##GC+$Kl# #'##X 8# #k##GՁ+##L##9~\<$##6##^ $##.(####]k# #)##eL# #@##l.##:##h'cܻ##sQ##.v3##.##z ̈́*##883^``jooR  3ooT6 q/##)ћR$##)}6#"  )U&##2n<##2 #2>;8<8W$4{%<27L.S  _)ԚG$o%͇7 N Pԥg* ֟>0vժk/Ր4 Ri20Ng9 7b ƬY0,+AKT]gqwiWE2" &;588N  $t4l%  824  E.K !U)A  $b%v4 G  !H[) 9  .h_-  ~1  !J]0  .G\5 4W{ rP. , *$# #"!#,?I I>$# #"#$29GI I?%# #!#4MfH##-35CI IB(# #" #Gښ*##)33>I IF.# # %##$339GI I5# #" q##/335CI I<%# #! $##*33=I IB(# # M##%338FIIE(# # ##1335BIID'# # ܺ##/33=IIB&## 6##-338FII8## e##+3 34BIIF%## ~##)3 3PPWnrrL 8(8P PRgrrl# 8,2P P^qrrA 8/,P PVmrr\ 83&P PRfrrW 86 PP]qrrO 8 PVlrrF 8# PPQer> 8& PP\6 8*!PP( 8-!PPN  80!PPD 81"PP: 80%PP0 8.,PP% 8-4PPL 8+;PPA 8*BPP5 8(IPP) 8" OPPN  84-PPB 8,?PP6*,.0246886 !NPP+!#%'%0PPN APPD?PP8(PP, ;SglZH,  5::3  $`aalR  :*  Paae h%  :7  =aauy/ :%  )aak.  :-  Zaad~|*   :98866552  Qaaty(   96433"  IaajZ   3'  Aa ac}%   3*  9a arL   3,  1a aio   3/  )a ac|h   31  !aaq]   3!  ahS   3#  !aac{H   3&  "aap>   3(  "aa,   3+  #aa^!   3-  #aaQ   3.  $aaD   3-  (aa6   3+  2aa(   3*  ;aa\   3)  EaaM   3(  Naa=   3'  Waa-   3"  !`aa^!   30  3aaO   3*  Jaa?  )*,-/02332!  #^aa/   "#%&$  6aa_"   MaaQ   JaaA   +aa1   !@]syeR0  Faa^"  R!  !PaaK   /  $Uaa7   ["  &Ya a`%   !  0a aQ  g  Ja a=  M  !Wa a)  UT* *4  #  #  #  #  #  #    !/"K#j%&' ()&*9+N,C-/. / 0 1= 1 1 2; 27 21 2* 2$ 1 1 1 1e 1. 0 0 0Q / / /_ . ..m.--|--,,,>+++O% '*a)))r)#(((4'''F&@@ 背景       IlIxI |Lq}l B N J#.; BXC{EsG><;999887899:99999::44555555555555555556679:;;<<><;999887899:99999::44555555555555555556679:;;<<><;999887899:99999::44555555555555555556679:;;<<! 0& ,                                                  ##                     ! 0& ,                                                  ##                     ! 0& ,                                                  ##                     -+* ) (' & 456$ ""!!!! " ! !    " ! !   %$$#"!!          !-+* ) (' & 456$ ""!!!! " ! !    " ! !   %$$#"!!          !-+* ) (' & 456$ ""!!!! " ! !    " ! !   %$$#"!!          !<<<<<==>= <<<<<==>= <<<<<==>=            1111)) ) ) )))*)(''&%%$##"      !!!!           1111)) ) ) )))*)(''&%%$##"      !!!!           1111)) ) ) )))*)(''&%%$##"      !!!!    %(11    4 % #!    322100/..-,,+**( & # !        "     ,($ !     %(11    4 % #!    322100/..-,,+**( & # !        "     ,($ !     %(11    4 % #!    322100/..-,,+**( & # !        "     ,($ !  ,"  "                ,"  "                ,"  "               """"""""""""#######                                                           """"""""""""#######              M  M M  M M  M M  M M  M M  M M M MM M MM M MM MM MM M MMM  MMM                """"""""""""#######              M  M M  M M  M M  M M  M M  M M M MM M MM M MM MM MM M MMM  MMM                    !!"##$%&&&'''((()))**                                     !!"##$%&&&'''((()))**    M MM MMMM MMM MM MMMMMMM MMM MM  MM M MM MM M M M  MMM M M  M M M           !!"##$%&&&'''((()))**    M MM MMMM MMM MM MMMMMMM MMM MM  MM M MM MM M M M  MMM M M  M M M        v75420 . - , , v75420 . - , , v75420 . - , ,         є **********++++++++++++++++,,,,,,,,,,,,,,,---------......         є **********++++++++++++++++,,,,,,,,,,,,,,,---------......         є **********++++++++++++++++,,,,,,,,,,,,,,,---------......                                                                                                                                  ŔŔŔŔŔ                                                                                                                                                                                                                                                                              + + + * *є * ) ) ( ( ( ' ' & % % $ # "! !                                                                + + + * *є * ) ) ( ( ( ' ' & % % $ # "! !                                                                + + + * *є * ) ) ( ( ( ' ' & % % $ # "! !                                                               ...////// / / /...////// / / /...////// / / /                                          }>C!  Background      J7 JWKKL  JJJJJKKK#K3KCKSKcKsKKKKKKK}>C!jenkins-remoting-2.23/src/0000755000175000017500000000000012122627370016166 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/0000755000175000017500000000000012122627370017453 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/resources/0000755000175000017500000000000012122627370021465 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/resources/jenkins/0000755000175000017500000000000012122627370023126 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/resources/jenkins/remoting/0000755000175000017500000000000012122627370024752 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/resources/jenkins/remoting/jenkins-version.properties0000644000175000017500000000003012122627370032205 0ustar jamespagejamespageversion=${build.version}jenkins-remoting-2.23/src/filter/resources/hudson/0000755000175000017500000000000012122627370022765 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/resources/hudson/remoting/0000755000175000017500000000000012122627370024611 5ustar jamespagejamespagejenkins-remoting-2.23/src/filter/resources/hudson/remoting/hudson-version.properties0000644000175000017500000000003012122627370031703 0ustar jamespagejamespageversion=${build.version}jenkins-remoting-2.23/src/main/0000755000175000017500000000000012122627370017112 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/java/0000755000175000017500000000000012122627370020033 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/java/hudson/0000755000175000017500000000000012122627370021333 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/java/hudson/remoting/0000755000175000017500000000000012122627370023157 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/java/hudson/remoting/Callable.java0000644000175000017500000000301112122627370025514 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.Serializable; /** * Represents computation to be done on a remote system. * * @see Channel * @author Kohsuke Kawaguchi */ public interface Callable extends Serializable { /** * Performs computation and returns the result, * or throws some exception. */ V call() throws T; } jenkins-remoting-2.23/src/main/java/hudson/remoting/ProxyException.java0000644000175000017500000000403512122627370027024 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; /** * Used when the exception thrown by the remoted code cannot be serialized. * *

* This exception captures the part of the information of the original exception * so that the caller can get some information about the problem that happened. * * @author Kohsuke Kawaguchi */ public class ProxyException extends IOException { public ProxyException(Throwable cause) { super(cause.toString()); // use toString() to capture the class name and error message setStackTrace(cause.getStackTrace()); // wrap all the chained exceptions if(cause.getCause()!=null) initCause(new ProxyException(cause.getCause())); } /** * {@link ProxyException} all the way down. */ @Override public ProxyException getCause() { return (ProxyException)super.getCause(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/ProxyOutputStream.java0000644000175000017500000004005012122627370027537 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InterruptedIOException; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; /** * {@link OutputStream} that sends bits to an exported * {@link OutputStream} on a remote machine. */ final class ProxyOutputStream extends OutputStream { private Channel channel; private int oid; private PipeWindow window; /** * If bytes are written to this stream before it's connected * to a remote object, bytes will be stored in this buffer. */ private ByteArrayOutputStream tmp; /** * Set to true if the stream is closed. */ private boolean closed; /** * Creates unconnected {@link ProxyOutputStream}. * The returned stream accepts data right away, and * when it's {@link #connect(Channel,int) connected} later, * the data will be sent at once to the remote stream. */ public ProxyOutputStream() { } /** * Creates an already connected {@link ProxyOutputStream}. * * @param oid * The object id of the exported {@link OutputStream}. */ public ProxyOutputStream(Channel channel, int oid) throws IOException { connect(channel,oid); } /** * Connects this stream to the specified remote object. */ synchronized void connect(Channel channel, int oid) throws IOException { if(this.channel!=null) throw new IllegalStateException("Cannot connect twice"); if(oid==0) throw new IllegalArgumentException("oid=0"); this.channel = channel; this.oid = oid; window = channel.getPipeWindow(oid); // if we already have bytes to write, do so now. if(tmp!=null) { byte[] b = tmp.toByteArray(); tmp = null; _write(b,0,b.length); } if(closed) // already marked closed? doClose(); } public void write(int b) throws IOException { write(new byte[]{(byte)b},0,1); } public void write(byte b[], int off, int len) throws IOException { if(closed) throw new IOException("stream is already closed"); _write(b, off, len); } /** * {@link #write(byte[])} without the close check. */ private synchronized void _write(byte[] b, int off, int len) throws IOException { if(channel==null) { if(tmp==null) tmp = new ByteArrayOutputStream(); tmp.write(b,off,len); } else { final int max = window.max(); while (len>0) { int sendable; try { /* To avoid fragmentation of the pipe window, at least demand that 10% of the pipe window be reclaimed. Imagine a large latency network where we are always low on the window size, and we are continuously sending data of irregular size. In such a circumstance, a fragmentation will happen. We start sending out a small Chunk at a time (say 4 bytes), and when its Ack comes back, it gets immediately consumed by another out-bound Chunk of 4 bytes. Clearly, it's better to wait a bit until we have a sizable pipe window, then send out a bigger Chunk, since Chunks have static overheads. This code does just that. (Except when what we are trying to send as a whole is smaller than the current available window size, in which case there's no point in waiting.) */ sendable = Math.min(window.get(Math.min(max/10,len)),len); /* Imagine if we have a lot of data to send and the pipe window is fully available. If we create one Chunk that fully uses the window size, we need to wait for the whole Chunk to get to the other side, then the Ack to come back to this side, before we can send a next Chunk. While the Ack is traveling back to us, we have to sit idle. This fails to utilize available bandwidth. A better strategy is to create a smaller Chunk, say half the window size. This allows the other side to send back the ack while we are sending the second Chunk. In a network with a non-trivial latency, this allows Chunk and Ack to overlap, and that improves the utilization. It's not clear what the best size of the chunk to send (there's a certain overhead in our Command structure, around 100-200 bytes), so I'm just starting with 2. Further analysis would be needed to determine the best value. */ sendable = Math.min(sendable, max /2); } catch (InterruptedException e) { throw (IOException)new InterruptedIOException().initCause(e); } channel.send(new Chunk(channel.newIoId(),oid,b,off,sendable)); window.decrease(sendable); off+=sendable; len-=sendable; } } } public synchronized void flush() throws IOException { if(channel!=null) channel.send(new Flush(channel.newIoId(),oid)); } public synchronized void close() throws IOException { closed = true; if(channel!=null) doClose(); } private void doClose() throws IOException { channel.send(new EOF(channel.newIoId(),oid)); channel = null; oid = -1; } @Override protected void finalize() throws Throwable { super.finalize(); // if we haven't done so, release the exported object on the remote side. if(channel!=null) { channel.send(new Unexport(channel.newIoId(),oid)); channel = null; oid = -1; } } /** * I/O operations in remoting gets executed by a separate pipe thread asynchronously. * So if a closure performs some I/O (such as writing to the RemoteOutputStream) then returns, * it is possible that the calling thread unblocks before the I/O actually completes. *

* This race condition creates a truncation problem like JENKINS-9189 or JENKINS-7871. * The initial fix for this was to introduce {@link Channel#syncLocalIO()}, but given the * recurrence in JENKINS-9189, I concluded that it's too error prone to expect the user of the * remoting to make such a call in the right place. *

* So the goal of this code is to automatically ensure the proper ordering of the return from * the {@link Request#call(Channel)} and the I/O operations done during the call. We do this * by attributing I/O call to a {@link Request}, then keeping track of the last I/O operation * performed. * * @deprecated as of 2.16 * {@link PipeWriter} does this job better, but kept for backward compatibility to communicate * with earlier version of remoting without losing the original fix to JENKINS-9189 completely. */ private static void markForIoSync(Channel channel, int requestId, java.util.concurrent.Future ioOp) { Request call = channel.pendingCalls.get(requestId); // call==null if: // 1) the remote peer uses old version that doesn't set the requestId field // 2) a bug in the code, but in that case we are being defensive if (call!=null) call.lastIo = ioOp; } /** * {@link Command} for sending bytes. */ private static final class Chunk extends Command { private final int oid; private final int ioId; private final int requestId = Request.getCurrentRequestId(); private final byte[] buf; public Chunk(int ioId, int oid, byte[] buf, int start, int len) { // to improve the performance when a channel is used purely as a pipe, // don't record the stack trace. On FilePath.writeToTar case, the stack trace and the OOS header // takes up about 1.5K. super(false); this.ioId = ioId; this.oid = oid; if (start==0 && len==buf.length) this.buf = buf; else { this.buf = new byte[len]; System.arraycopy(buf,start,this.buf,0,len); } } protected void execute(final Channel channel) { final OutputStream os = (OutputStream) channel.getExportedObject(oid); markForIoSync(channel,requestId,channel.pipeWriter.submit(ioId,new Runnable() { public void run() { try { os.write(buf); } catch (IOException e) { try { channel.send(new NotifyDeadWriter(channel,e,oid)); } catch (ChannelClosedException x) { // the other direction can be already closed if the connection // shut down is initiated from this side. In that case, remain silent. } catch (IOException x) { // ignore errors LOGGER.log(Level.WARNING, "Failed to notify the sender that the write end is dead",x); LOGGER.log(Level.WARNING, "... the failed write was:",e); } } finally { if (channel.remoteCapability.supportsPipeThrottling()) { try { channel.send(new Ack(oid,buf.length)); } catch (ChannelClosedException x) { // the other direction can be already closed if the connection // shut down is initiated from this side. In that case, remain silent. } catch (IOException e) { // ignore errors LOGGER.log(Level.WARNING, "Failed to ack the stream",e); } } } } })); } public String toString() { return "Pipe.Chunk("+oid+","+buf.length+")"; } private static final long serialVersionUID = 1L; } /** * {@link Command} for flushing. */ private static final class Flush extends Command { private final int oid; private final int requestId = Request.getCurrentRequestId(); private final int ioId; public Flush(int ioId, int oid) { super(false); this.ioId = ioId; this.oid = oid; } protected void execute(Channel channel) { final OutputStream os = (OutputStream) channel.getExportedObject(oid); markForIoSync(channel,requestId,channel.pipeWriter.submit(ioId,new Runnable() { public void run() { try { os.flush(); } catch (IOException e) { // ignore errors } } })); } public String toString() { return "Pipe.Flush("+oid+")"; } private static final long serialVersionUID = 1L; } /** * {@link Command} for releasing an export table. * *

* Unlike {@link EOF}, this just unexports but not closes the stream. */ private static class Unexport extends Command { private final int oid; private final int ioId; public Unexport(int ioId, int oid) { this.ioId = ioId; this.oid = oid; } protected void execute(final Channel channel) { channel.pipeWriter.submit(ioId,new Runnable() { public void run() { channel.unexport(oid); } }); } public String toString() { return "Pipe.Unexport("+oid+")"; } private static final long serialVersionUID = 1L; } /** * {@link Command} for sending EOF. */ private static final class EOF extends Command { private final int oid; private final int requestId = Request.getCurrentRequestId(); private final int ioId; public EOF(int ioId, int oid) { this.ioId = ioId; this.oid = oid; } protected void execute(final Channel channel) { final OutputStream os = (OutputStream) channel.getExportedObject(oid); markForIoSync(channel,requestId,channel.pipeWriter.submit(ioId,new Runnable() { public void run() { channel.unexport(oid); try { os.close(); } catch (IOException e) { // ignore errors } } })); } public String toString() { return "Pipe.EOF("+oid+")"; } private static final long serialVersionUID = 1L; } /** * {@link Command} to notify the sender that it can send some more data. */ private static class Ack extends Command { /** * The oid of the {@link OutputStream} on the receiver side of the data. */ private final int oid; /** * The number of bytes that were freed up. */ private final int size; private Ack(int oid, int size) { super(false); // performance optimization this.oid = oid; this.size = size; } protected void execute(Channel channel) { PipeWindow w = channel.getPipeWindow(oid); w.increase(size); } public String toString() { return "Pipe.Ack("+oid+','+size+")"; } private static final long serialVersionUID = 1L; } /** * {@link Command} to notify the sender that the receiver is dead. */ private static final class NotifyDeadWriter extends Command { private final int oid; private NotifyDeadWriter(Channel channel,Throwable cause, int oid) { super(channel,cause); this.oid = oid; } @Override protected void execute(Channel channel) { PipeWindow w = channel.getPipeWindow(oid); w.dead(createdAt.getCause()); } public String toString() { return "Pipe.Dead("+oid+")"; } private static final long serialVersionUID = 1L; } private static final Logger LOGGER = Logger.getLogger(ProxyOutputStream.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/DelegatingExecutorService.java0000644000175000017500000000400012122627370031117 0ustar jamespagejamespagepackage hudson.remoting; import java.util.Collection; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; /** * {@link ExecutorService} that delegates to another one. * * @author Kohsuke Kawaguchi */ class DelegatingExecutorService implements ExecutorService { private final ExecutorService base; public DelegatingExecutorService(ExecutorService base) { this.base = base; } public void shutdown() { base.shutdown(); } public List shutdownNow() { return base.shutdownNow(); } public boolean isShutdown() { return base.isShutdown(); } public boolean isTerminated() { return base.isTerminated(); } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return base.awaitTermination(timeout, unit); } public Future submit(Callable task) { return base.submit(task); } public Future submit(Runnable task, T result) { return base.submit(task, result); } public Future submit(Runnable task) { return base.submit(task); } public List> invokeAll(Collection> tasks) throws InterruptedException { return base.invokeAll(tasks); } public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { return base.invokeAll(tasks, timeout, unit); } public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { return base.invokeAny(tasks); } public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return base.invokeAny(tasks, timeout, unit); } public void execute(Runnable command) { base.execute(command); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/MultiClassLoaderSerializer.java0000644000175000017500000001255212122627370031270 0ustar jamespagejamespagepackage hudson.remoting; import hudson.remoting.RemoteClassLoader.IClassLoader; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * {@link ObjectInputStream}/{@link ObjectOutputStream} pair that can handle object graph that spans across * multiple classloaders. * *

* To pass around ClassLoaders, this class uses OID instead of {@link IClassLoader}, since doing so * can results in recursive class resolution that may end up with NPE in ObjectInputStream.defaultReadFields like * described in the comment from huybrechts in HUDSON-4293. * * @author Kohsuke Kawaguchi * @see Capability#supportsMultiClassLoaderRPC() */ class MultiClassLoaderSerializer { static final class Output extends ObjectOutputStream { private final Channel channel; /** * Encountered Classloaders, to their indices. */ private final Map classLoaders = new HashMap(); Output(Channel channel, OutputStream out) throws IOException { super(out); this.channel = channel; } @Override protected void annotateClass(Class c) throws IOException { ClassLoader cl = c.getClassLoader(); if (cl==null) {// bootstrap classloader. no need to export. writeInt(TAG_SYSTEMCLASSLOADER); return; } Integer idx = classLoaders.get(cl); if (idx==null) { classLoaders.put(cl,classLoaders.size()); if (cl instanceof RemoteClassLoader) { int oid = ((RemoteClassLoader) cl).getOid(channel); if (oid>=0) { // this classloader came from where we are sending this classloader to. writeInt(TAG_LOCAL_CLASSLOADER); writeInt(oid); return; } } // tell the receiving side that they need to import a new classloader // this reference count is released when RemoteInvocationHandler backing IClassLoader is GCed on the remote node. writeInt(TAG_EXPORTED_CLASSLOADER); writeInt(RemoteClassLoader.exportId(cl,channel)); } else {// reference to a classloader that's already written writeInt(idx); } } @Override protected void annotateProxyClass(Class cl) throws IOException { annotateClass(cl); } } static final class Input extends ObjectInputStream { private final Channel channel; private final List classLoaders = new ArrayList(); Input(Channel channel, InputStream in) throws IOException { super(in); this.channel = channel; } private ClassLoader readClassLoader() throws IOException, ClassNotFoundException { ClassLoader cl; int code = readInt(); switch (code) { case TAG_SYSTEMCLASSLOADER: return null; case TAG_LOCAL_CLASSLOADER: cl = ((RemoteClassLoader.ClassLoaderProxy)channel.getExportedObject(readInt())).cl; classLoaders.add(cl); return cl; case TAG_EXPORTED_CLASSLOADER: cl = channel.importedClassLoaders.get(readInt()); classLoaders.add(cl); return cl; default: return classLoaders.get(code); } } @Override protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); try { ClassLoader cl = readClassLoader(); Class c = Class.forName(name, false, cl); return c; } catch (ClassNotFoundException ex) { return super.resolveClass(desc); } } @Override protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { ClassLoader cl = readClassLoader(); Class[] classes = new Class[interfaces.length]; for (int i = 0; i < interfaces.length; i++) classes[i] = Class.forName(interfaces[i], false, cl); return Proxy.getProxyClass(cl, classes); } } /** * Indicates that the class being sent should be loaded from the system classloader. */ private static final int TAG_SYSTEMCLASSLOADER = -3; /** * Indicates that the class being sent originates from the sender side. The sender exports this classloader * and sends its OID in the following int. The receiver will import this classloader to resolve the class. */ private static final int TAG_EXPORTED_CLASSLOADER = -2; /** * Indicates that the class being sent originally came from the receiver. The following int indicates * the OID of the classloader exported from the receiver, which the sender used. */ private static final int TAG_LOCAL_CLASSLOADER = -1; } jenkins-remoting-2.23/src/main/java/hudson/remoting/Pipe.java0000644000175000017500000001632312122627370024724 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger; /** * Pipe for the remote {@link Callable} and the local program to talk to each other. * *

* There are two kinds of pipes. One is for having a local system write to a remote system, * and the other is for having a remote system write to a local system. Use * the different versions of the create method to create the appropriate kind * of pipes. * *

* Once created, {@link Pipe} can be sent to the remote system as a part of a serialization of * {@link Callable} between {@link Channel}s. * Once re-instantiated on the remote {@link Channel}, pipe automatically connects * back to the local instance and perform necessary set up. * *

* The local and remote system can then call {@link #getIn()} and {@link #getOut()} to * read/write bytes. * *

* Pipe can be only written by one system and read by the other system. It is an error to * send one {@link Pipe} to two remote {@link Channel}s, or send one {@link Pipe} to * the same {@link Channel} twice. * *

Usage

*
 * final Pipe p = Pipe.createLocalToRemote();
 *
 * channel.callAsync(new Callable() {
 *   public Object call() {
 *     InputStream in = p.getIn();
 *     ... read from in ...
 *   }
 * });
 *
 * OutputStream out = p.getOut();
 * ... write to out ...
 * 
* * Similarly, for remote to local pipe, * *
 * final Pipe p = Pipe.createRemoteToLocal();
 *
 * channel.callAsync(new Callable() {
 *   public Object call() {
 *     OutputStream out = p.getOut();
 *     ... write to out ...
 *   }
 * });
 *
 * InputStream in = p.getIn();
 * ... read from in ...
 * 
* *

Implementation Note

*

* For better performance, {@link Pipe} uses lower-level {@link Command} abstraction * to send data, instead of typed proxy object. This allows the writer to send data * without blocking until the arrival of the data is confirmed. * * @author Kohsuke Kawaguchi */ public final class Pipe implements Serializable { private InputStream in; private OutputStream out; private Pipe(InputStream in, OutputStream out) { this.in = in; this.out = out; } /** * Gets the reading end of the pipe. */ public InputStream getIn() { return in; } /** * Gets the writing end of the pipe. */ public OutputStream getOut() { return out; } /** * Creates a {@link Pipe} that allows remote system to write and local system to read. */ public static Pipe createRemoteToLocal() { // OutputStream will be created on the target return new Pipe(new FastPipedInputStream(),null); } /** * Creates a {@link Pipe} that allows local system to write and remote system to read. */ public static Pipe createLocalToRemote() { return new Pipe(null,new ProxyOutputStream()); } private void writeObject(ObjectOutputStream oos) throws IOException { if(in!=null && out==null) { // remote will write to local FastPipedOutputStream pos = new FastPipedOutputStream((FastPipedInputStream)in); int oid = Channel.current().export(pos,false); // this export is unexported in ProxyOutputStream.finalize() oos.writeBoolean(true); // marker oos.writeInt(oid); } else { // remote will read from local this object gets unexported when the pipe is connected. // see ConnectCommand int oid = Channel.current().export(out,false); oos.writeBoolean(false); oos.writeInt(oid); } } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { final Channel channel = Channel.current(); assert channel !=null; if(ois.readBoolean()) { // local will write to remote in = null; out = new ProxyOutputStream(channel, ois.readInt()); } else { // local will read from remote. // tell the remote system about this local read pipe // this is the OutputStream that wants to send data to us final int oidRos = ois.readInt(); // we want 'oidRos' to send data to this PipedOutputStream FastPipedOutputStream pos = new FastPipedOutputStream(); FastPipedInputStream pis = new FastPipedInputStream(pos); final int oidPos = channel.export(pos,false); // this gets unexported when the remote ProxyOutputStream closes. // tell 'ros' to connect to our 'pos'. channel.send(new ConnectCommand(oidRos, oidPos)); out = null; in = pis; } } private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(Pipe.class.getName()); private static class ConnectCommand extends Command { private final int oidRos; private final int oidPos; public ConnectCommand(int oidRos, int oidPos) { this.oidRos = oidRos; this.oidPos = oidPos; } protected void execute(final Channel channel) { // ordering barrier not needed for this I/O call, so not giving I/O ID. channel.pipeWriter.submit(0,new Runnable() { public void run() { try { final ProxyOutputStream ros = (ProxyOutputStream) channel.getExportedObject(oidRos); channel.unexport(oidRos); // the above unexport cancels out the export in writeObject above ros.connect(channel, oidPos); } catch (IOException e) { logger.log(Level.SEVERE,"Failed to connect to pipe",e); } } }); } static final long serialVersionUID = -9128735897846418140L; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/SocketOutputStream.java0000644000175000017500000000432612122627370027654 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; /** * {@link InputStream} connected to socket. * *

* Unlike plain {@link Socket#getOutputStream()}, closing the stream * does not close the entire socket, and instead it merely partial-close * a socket in the direction. * * @author Kohsuke Kawaguchi */ public class SocketOutputStream extends FilterOutputStream { private final Socket socket; public SocketOutputStream(Socket socket) throws IOException { super(socket.getOutputStream()); this.socket = socket; } public void write(int b) throws IOException { out.write(b); } public void write(byte[] b) throws IOException { out.write(b); } public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); } @Override public void close() throws IOException { socket.shutdownOutput(); if (socket.isInputShutdown()) socket.close(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/FastPipedOutputStream.java0000644000175000017500000001501612122627370030301 0ustar jamespagejamespage/* * @(#)$Id: FastPipedOutputStream.java 3619 2008-03-26 07:23:03Z yui $ * * Copyright 2006-2008 Makoto YUI * * Licensed 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. * * Contributors: * Makoto YUI - initial implementation */ package hudson.remoting; import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.IOException; import java.lang.ref.WeakReference; /** * This class is equivalent to java.io.PipedOutputStream. In the * interface it only adds a constructor which allows for specifying the buffer * size. Its implementation, however, is much simpler and a lot more efficient * than its equivalent. It doesn't rely on polling. Instead it uses proper * synchronization with its counterpart {@link FastPipedInputStream}. * * @author WD * @link http://developer.java.sun.com/developer/bugParade/bugs/4404700.html * @see FastPipedOutputStream */ public class FastPipedOutputStream extends OutputStream { WeakReference sink; /** * Keeps track of the total # of bytes written via this output stream. * Helps with debugging, and serves no other purpose. */ private long written=0; private final Throwable allocatedAt = new Throwable(); /** * Creates an unconnected PipedOutputStream. */ public FastPipedOutputStream() { super(); } /** * Creates a PipedOutputStream with a default buffer size and connects it to * sink. * @exception IOException It was already connected. */ public FastPipedOutputStream(FastPipedInputStream sink) throws IOException { connect(sink); } /** * Creates a PipedOutputStream with buffer size bufferSize and * connects it to sink. * @exception IOException It was already connected. * @deprecated as of 1.350 * bufferSize parameter is ignored. */ public FastPipedOutputStream(FastPipedInputStream sink, int bufferSize) throws IOException { this(sink); } private FastPipedInputStream sink() throws IOException { FastPipedInputStream s = sink.get(); if (s==null) throw (IOException)new IOException("Reader side has already been abandoned").initCause(allocatedAt); return s; } /** * @exception IOException The pipe is not connected. */ @Override public void close() throws IOException { if(sink == null) { throw new IOException("Unconnected pipe"); } FastPipedInputStream s = sink(); synchronized(s.buffer) { s.closed = new FastPipedInputStream.ClosedBy(); flush(); } } /** * @exception IOException The pipe is already connected. */ public void connect(FastPipedInputStream sink) throws IOException { if(this.sink != null) { throw new IOException("Pipe already connected"); } this.sink = new WeakReference(sink); sink.source = new WeakReference(this); } @Override protected void finalize() throws Throwable { super.finalize(); close(); } @Override public void flush() throws IOException { FastPipedInputStream s = sink(); synchronized(s.buffer) { // Release all readers. s.buffer.notifyAll(); } } public void write(int b) throws IOException { write(new byte[] { (byte) b }); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } /** * @exception IOException The pipe is not connected or a reader has closed it. */ @Override public void write(byte[] b, int off, int len) throws IOException { if(sink == null) { throw new IOException("Unconnected pipe"); } while (len>0) { FastPipedInputStream s = sink(); // make sure the sink is still trying to read, or else fail the write. if(s.closed!=null) { throw (IOException)new IOException("Pipe is already closed").initCause(s.closed); } synchronized(s.buffer) { if(s.writePosition == s.readPosition && s.writeLaps > s.readLaps) { // The circular buffer is full, so wait for some reader to consume // something. // release a reference to 's' during the wait so that if the reader has abandoned the pipe // we can tell. byte[] buf = s.buffer; s = null; Thread t = Thread.currentThread(); String oldName = t.getName(); t.setName("Blocking to write '"+HexDump.toHex(b,off,Math.min(len,256))+"' : "+oldName); try { buf.wait(TIMEOUT); } catch (InterruptedException e) { throw (InterruptedIOException)new InterruptedIOException(e.getMessage()).initCause(e); } finally { t.setName(oldName); } // Try again. continue; } // Don't write more than the capacity indicated by len or the space // available in the circular buffer. int amount = Math.min(len, (s.writePosition < s.readPosition ? s.readPosition : s.buffer.length) - s.writePosition); System.arraycopy(b, off, s.buffer, s.writePosition, amount); s.writePosition += amount; if(s.writePosition == s.buffer.length) { s.writePosition = 0; ++s.writeLaps; } off += amount; len -= amount; written += amount; s.buffer.notifyAll(); } } } static final int TIMEOUT = Integer.getInteger(FastPipedOutputStream.class.getName()+".timeout",10*1000); } jenkins-remoting-2.23/src/main/java/hudson/remoting/SynchronousCommandTransport.java0000644000175000017500000000626112122627370031575 0ustar jamespagejamespagepackage hudson.remoting; import java.io.EOFException; import java.io.IOException; import java.io.InterruptedIOException; import java.util.logging.Level; import java.util.logging.Logger; /** * {@link CommandTransport} that implements the read operation in a synchronous fashion. * *

* This class uses a thread to pump commands and pass them to {@link CommandReceiver}. * * @author Kohsuke Kawaguchi */ public abstract class SynchronousCommandTransport extends CommandTransport { protected Channel channel; /** * Called by {@link Channel} to read the next command to arrive from the stream. */ abstract Command read() throws IOException, ClassNotFoundException, InterruptedException; @Override public void setup(Channel channel, CommandReceiver receiver) { this.channel = channel; new ReaderThread(receiver).start(); } private final class ReaderThread extends Thread { private int commandsReceived = 0; private int commandsExecuted = 0; private final CommandReceiver receiver; public ReaderThread(CommandReceiver receiver) { super("Channel reader thread: "+channel.getName()); this.receiver = receiver; } @Override public void run() { final String name =channel.getName(); try { while(!channel.isInClosed()) { Command cmd = null; try { cmd = read(); } catch (EOFException e) { IOException ioe = new IOException("Unexpected termination of the channel"); ioe.initCause(e); throw ioe; } catch (ClassNotFoundException e) { LOGGER.log(Level.SEVERE, "Unable to read a command (channel " + name + ")",e); continue; } finally { commandsReceived++; } receiver.handle(cmd); commandsExecuted++; } closeRead(); } catch (InterruptedException e) { LOGGER.log(Level.SEVERE, "I/O error in channel "+name,e); channel.terminate((InterruptedIOException) new InterruptedIOException().initCause(e)); } catch (IOException e) { LOGGER.log(Level.SEVERE, "I/O error in channel "+name,e); channel.terminate(e); } catch (RuntimeException e) { LOGGER.log(Level.SEVERE, "Unexpected error in channel "+name,e); channel.terminate((IOException) new IOException("Unexpected reader termination").initCause(e)); throw e; } catch (Error e) { LOGGER.log(Level.SEVERE, "Unexpected error in channel "+name,e); channel.terminate((IOException) new IOException("Unexpected reader termination").initCause(e)); throw e; } finally { channel.pipeWriter.shutdown(); } } } private static final Logger LOGGER = Logger.getLogger(SynchronousCommandTransport.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/Which.java0000644000175000017500000002262512122627370025073 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Ullrich Hafner * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.MalformedURLException; import java.net.URLConnection; import java.net.JarURLConnection; import java.lang.reflect.Field; import java.util.zip.ZipFile; import java.util.jar.JarFile; import java.util.logging.Logger; import java.util.logging.Level; /** * Locates where a given class is loaded from. * * @author Kohsuke Kawaguchi */ public class Which { /** * Locates the jar file that contains the given class. * * @throws IllegalArgumentException * if failed to determine. */ public static URL jarURL(Class clazz) throws IOException { ClassLoader cl = clazz.getClassLoader(); if(cl==null) cl = ClassLoader.getSystemClassLoader(); URL res = cl.getResource(clazz.getName().replace('.', '/') + ".class"); if(res==null) throw new IllegalArgumentException("Unable to locate class file for "+clazz); return res; } /** * Locates the jar file that contains the given class. * *

* Note that jar files are not always loaded from {@link File}, * so for diagnostics purposes {@link #jarURL(Class)} is preferrable. * * @throws IllegalArgumentException * if failed to determine. */ public static File jarFile(Class clazz) throws IOException { URL res = jarURL(clazz); String resURL = res.toExternalForm(); String originalURL = resURL; if(resURL.startsWith("jar:file:") || resURL.startsWith("wsjar:file:")) return fromJarUrlToFile(resURL); if(resURL.startsWith("code-source:/")) { // OC4J apparently uses this. See http://www.nabble.com/Hudson-on-OC4J-tt16702113.html resURL = resURL.substring("code-source:/".length(), resURL.lastIndexOf('!')); // cut off jar: and the file name portion return new File(decode(new URL("file:/"+resURL).getPath())); } if(resURL.startsWith("zip:")){ // weblogic uses this. See http://www.nabble.com/patch-to-get-Hudson-working-on-weblogic-td23997258.html // also see http://www.nabble.com/Re%3A-Hudson-on-Weblogic-10.3-td25038378.html#a25043415 resURL = resURL.substring("zip:".length(), resURL.lastIndexOf('!')); // cut off zip: and the file name portion return new File(decode(new URL("file:"+resURL).getPath())); } if(resURL.startsWith("file:")) { // unpackaged classes int n = clazz.getName().split("\\.").length; // how many slashes do wo need to cut? for( ; n>0; n-- ) { int idx = Math.max(resURL.lastIndexOf('/'), resURL.lastIndexOf('\\')); if(idx<0) throw new IllegalArgumentException(originalURL + " - " + resURL); resURL = resURL.substring(0,idx); } // won't work if res URL contains ' ' // return new File(new URI(null,new URL(res).toExternalForm(),null)); // won't work if res URL contains '%20' // return new File(new URL(res).toURI()); return new File(decode(new URL(resURL).getPath())); } if(resURL.startsWith("vfszip:")) { // JBoss5 InputStream is = res.openStream(); try { Object delegate = is; while (delegate.getClass().getEnclosingClass()!=ZipFile.class) { Field f = delegate.getClass().getDeclaredField("delegate"); f.setAccessible(true); delegate = f.get(delegate); //JENKINS-5922 - workaround for CertificateReaderInputStream; JBoss 5.0.0, EAP 5.0 and EAP 5.1 if(delegate.getClass().getName().equals("java.util.jar.JarVerifier$VerifierStream")){ f = delegate.getClass().getDeclaredField("is"); f.setAccessible(true); delegate = f.get(delegate); } } Field f = delegate.getClass().getDeclaredField("this$0"); f.setAccessible(true); ZipFile zipFile = (ZipFile)f.get(delegate); return new File(zipFile.getName()); } catch (NoSuchFieldException e) { // something must have changed in JBoss5. fall through LOGGER.log(Level.FINE, "Failed to resolve vfszip into a jar location",e); } catch (IllegalAccessException e) { // something must have changed in JBoss5. fall through LOGGER.log(Level.FINE, "Failed to resolve vfszip into a jar location",e); } finally { is.close(); } } if(resURL.startsWith("vfs:")) { // JBoss6 String dotdot=""; for (int i=clazz.getName().split("\\.").length; i>1; i--) dotdot+="../"; try { URL jar = new URL(res,dotdot); String path = jar.getPath(); if (path.endsWith("/")) path=path.substring(0,path.length()-1); // obtain the file name portion String fileName = path.substring(path.lastIndexOf('/')+1); Object vfs = new URL(jar,"..").getContent(); // a VirtualFile object pointing to the parent of the jar File dir = (File)vfs.getClass().getMethod("getPhysicalFile").invoke(vfs); File jarFile = new File(dir,fileName); if (jarFile.exists()) return jarFile; } catch (Exception e) { LOGGER.log(Level.FINE, "Failed to resolve vfs file into a location",e); } } URLConnection con = res.openConnection(); if (con instanceof JarURLConnection) { JarURLConnection jcon = (JarURLConnection) con; JarFile jarFile = jcon.getJarFile(); if (jarFile!=null) { String n = jarFile.getName(); if(n.length()>0) {// JDK6u10 needs this return new File(n); } else { // JDK6u10 apparently starts hiding the real jar file name, // so this just keeps getting tricker and trickier... try { Field f = ZipFile.class.getDeclaredField("name"); f.setAccessible(true); return new File((String) f.get(jarFile)); } catch (NoSuchFieldException e) { LOGGER.log(Level.INFO, "Failed to obtain the local cache file name of "+clazz, e); } catch (IllegalAccessException e) { LOGGER.log(Level.INFO, "Failed to obtain the local cache file name of "+clazz, e); } } } } throw new IllegalArgumentException(originalURL + " - " + resURL); } public static File jarFile(URL resource) throws IOException { return fromJarUrlToFile(resource.toExternalForm()); } private static File fromJarUrlToFile(String resURL) throws MalformedURLException { resURL = resURL.substring(resURL.indexOf(':')+1, resURL.lastIndexOf('!')); // cut off "scheme:" and the file name portion return new File(decode(new URL(resURL).getPath())); } /** * Decode '%HH'. */ private static String decode(String s) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); for( int i=0; i>4)&15)); r.append(CODE.charAt(b&15)); } return r.toString(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/Launcher.java0000644000175000017500000005443212122627370025573 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.Channel.Mode; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.kohsuke.args4j.Option; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.CmdLineException; import javax.crypto.spec.IvParameterSpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.TrustManager; import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.File; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileWriter; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLConnection; import java.net.ServerSocket; import java.net.Socket; import java.net.URLClassLoader; import java.net.InetSocketAddress; import java.net.HttpURLConnection; import java.net.Authenticator; import java.net.PasswordAuthentication; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.NoSuchAlgorithmException; import java.security.KeyManagementException; import java.security.SecureRandom; import java.util.Properties; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; /** * Entry point for running a {@link Channel}. This is the main method of the slave JVM. * *

* This class also defines several methods for * starting a channel on a fresh JVM. * * @author Kohsuke Kawaguchi */ public class Launcher { public Mode mode = Mode.BINARY; // no-op, but left for backward compatibility @Option(name="-ping") public boolean ping = true; @Option(name="-slaveLog", usage="create local slave error log") public File slaveLog = null; @Option(name="-text",usage="encode communication with the master with base64. " + "Useful for running slave over 8-bit unsafe protocol like telnet") public void setTextMode(boolean b) { mode = b?Mode.TEXT:Mode.BINARY; System.out.println("Running in "+mode.name().toLowerCase(Locale.ENGLISH)+" mode"); } @Option(name="-jnlpUrl",usage="instead of talking to the master via stdin/stdout, " + "emulate a JNLP client by making a TCP connection to the master. " + "Connection parameters are obtained by parsing the JNLP file.") public URL slaveJnlpURL = null; @Option(name="-jnlpCredentials",metaVar="USER:PASSWORD",usage="HTTP BASIC AUTH header to pass in for making HTTP requests.") public String slaveJnlpCredentials = null; @Option(name="-secret", metaVar="HEX_SECRET", usage="Slave connection secret to use instead of -jnlpCredentials.") public String secret; @Option(name="-cp",aliases="-classpath",metaVar="PATH", usage="add the given classpath elements to the system classloader.") public void addClasspath(String pathList) throws Exception { Method $addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); $addURL.setAccessible(true); for(String token : pathList.split(File.pathSeparator)) $addURL.invoke(ClassLoader.getSystemClassLoader(),new File(token).toURI().toURL()); // fix up the system.class.path to pretend that those jar files // are given through CLASSPATH or something. // some tools like JAX-WS RI and Hadoop relies on this. System.setProperty("java.class.path",System.getProperty("java.class.path")+File.pathSeparatorChar+pathList); } @Option(name="-tcp",usage="instead of talking to the master via stdin/stdout, " + "listens to a random local port, write that port number to the given file, " + "then wait for the master to connect to that port.") public File tcpPortFile=null; @Option(name="-auth",metaVar="user:pass",usage="If your Hudson is security-enabled, specify a valid user name and password.") public String auth = null; public InetSocketAddress connectionTarget = null; @Option(name="-connectTo",usage="make a TCP connection to the given host and port, then start communication.",metaVar="HOST:PORT") public void setConnectTo(String target) { String[] tokens = target.split(":"); if(tokens.length!=2) { System.err.println("Illegal parameter: "+target); System.exit(1); } connectionTarget = new InetSocketAddress(tokens[0],Integer.valueOf(tokens[1])); } /** * Bypass HTTPS security check by using free-for-all trust manager. * * @param _ * This is ignored. */ @Option(name="-noCertificateCheck") public void setNoCertificateCheck(boolean _) throws NoSuchAlgorithmException, KeyManagementException { System.out.println("Skipping HTTPS certificate checks altogether. Note that this is not secure at all."); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[]{new NoCheckTrustManager()}, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); // bypass host name check, too. HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String s, SSLSession sslSession) { return true; } }); } public static void main(String... args) throws Exception { Launcher launcher = new Launcher(); CmdLineParser parser = new CmdLineParser(launcher); try { parser.parseArgument(args); launcher.run(); } catch (CmdLineException e) { System.err.println(e.getMessage()); System.err.println("java -jar slave.jar [options...]"); parser.printUsage(System.err); System.err.println(); } } public void run() throws Exception { if (slaveLog!=null) { System.setErr(new PrintStream(new TeeOutputStream(System.err,new FileOutputStream(slaveLog)))); } if(auth!=null) { final int idx = auth.indexOf(':'); if(idx<0) throw new CmdLineException(null, "No ':' in the -auth option"); Authenticator.setDefault(new Authenticator() { @Override public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(auth.substring(0,idx), auth.substring(idx+1).toCharArray()); } }); } if(connectionTarget!=null) { runAsTcpClient(); System.exit(0); } else if(slaveJnlpURL!=null) { List jnlpArgs = parseJnlpArguments(); try { hudson.remoting.jnlp.Main._main(jnlpArgs.toArray(new String[jnlpArgs.size()])); } catch (CmdLineException e) { System.err.println("JNLP file "+slaveJnlpURL+" has invalid arguments: "+jnlpArgs); System.err.println("Most likely a configuration error in the master"); System.err.println(e.getMessage()); System.exit(1); } } else if(tcpPortFile!=null) { runAsTcpServer(); System.exit(0); } else { runWithStdinStdout(); System.exit(0); } } /** * Parses the connection arguments from JNLP file given in the URL. */ public List parseJnlpArguments() throws ParserConfigurationException, SAXException, IOException, InterruptedException { if (secret != null) { slaveJnlpURL = new URL(slaveJnlpURL + "?encrypt=true"); if (slaveJnlpCredentials != null) { throw new IOException("-jnlpCredentials and -secret are mutually exclusive"); } } while (true) { try { URLConnection con = slaveJnlpURL.openConnection(); if (con instanceof HttpURLConnection && slaveJnlpCredentials != null) { HttpURLConnection http = (HttpURLConnection) con; String userPassword = slaveJnlpCredentials; String encoding = Base64.encode(userPassword.getBytes()); http.setRequestProperty("Authorization", "Basic " + encoding); } con.connect(); if (con instanceof HttpURLConnection) { HttpURLConnection http = (HttpURLConnection) con; if(http.getResponseCode()>=400) // got the error code. report that (such as 401) throw new IOException("Failed to load "+slaveJnlpURL+": "+http.getResponseCode()+" "+http.getResponseMessage()); } Document dom; // check if this URL points to a .jnlp file String contentType = con.getHeaderField("Content-Type"); String expectedContentType = secret == null ? "application/x-java-jnlp-file" : "application/octet-stream"; InputStream input = con.getInputStream(); if (secret != null) { byte[] payload = toByteArray(input); // the first 16 bytes (128bit) are initialization vector try { Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(fromHexString(secret.substring(0, Math.min(secret.length(), 32))), "AES"), new IvParameterSpec(payload,0,16)); byte[] decrypted = cipher.doFinal(payload,16,payload.length-16); input = new ByteArrayInputStream(decrypted); } catch (GeneralSecurityException x) { throw (IOException)new IOException("Failed to decrypt the JNLP file. Invalid secret key?").initCause(x); } } if(contentType==null || !contentType.startsWith(expectedContentType)) { // load DOM anyway, but if it fails to parse, that's probably because this is not an XML file to begin with. try { dom = loadDom(slaveJnlpURL, input); } catch (SAXException e) { throw new IOException(slaveJnlpURL+" doesn't look like a JNLP file; content type was "+contentType); } catch (IOException e) { throw new IOException(slaveJnlpURL+" doesn't look like a JNLP file; content type was "+contentType); } } else { dom = loadDom(slaveJnlpURL, input); } // exec into the JNLP launcher, to fetch the connection parameter through JNLP. NodeList argElements = dom.getElementsByTagName("argument"); List jnlpArgs = new ArrayList(); for( int i=0; i 0 && interval > 0) { new PingThread(channel, timeout, interval) { @Override protected void onDead() { System.err.println("Ping failed. Terminating"); System.exit(-1); } }.start(); } channel.join(); System.err.println("channel stopped"); } /** * {@link X509TrustManager} that performs no check at all. */ private static class NoCheckTrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } public static boolean isWindows() { return File.pathSeparatorChar==';'; } private static String computeVersion() { Properties props = new Properties(); try { InputStream is = Launcher.class.getResourceAsStream("hudson-version.properties"); if(is!=null) props.load(is); } catch (IOException e) { e.printStackTrace(); } return props.getProperty("version", "?"); } /** * Version number of Hudson this slave.jar is from. */ public static final String VERSION = computeVersion(); } jenkins-remoting-2.23/src/main/java/hudson/remoting/Command.java0000644000175000017500000000752012122627370025404 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * One-way command to be sent over to the remote system and executed there. * This is layer 0, the lower most layer. * *

* At this level, remoting of class files are not provided, so both {@link Channel}s * need to have the definition of {@link Command}-implementation. * * @author Kohsuke Kawaguchi */ abstract class Command implements Serializable { /** * This exception captures the stack trace of where the Command object is created. * This is useful for diagnosing the error when command fails to execute on the remote peer. */ public final Exception createdAt; protected Command() { this(true); } protected Command(Channel channel, Throwable cause) { // Command object needs to be deserializable on the other end without requiring custom classloading, // so we wrap this in MimicException this.createdAt = new Source(MimicException.make(channel,cause)); } /** * @param recordCreatedAt * If false, skip the recording of where the command is created. This makes the trouble-shooting * and cause/effect correlation hard in case of a failure, but it will reduce the amount of the data * transferred. */ protected Command(boolean recordCreatedAt) { if(recordCreatedAt) this.createdAt = new Source(); else this.createdAt = null; } /** * Called on a remote system to perform this command. * * @param channel * The {@link Channel} of the remote system. */ protected abstract void execute(Channel channel); void writeTo(Channel channel, ObjectOutputStream oos) throws IOException { Channel old = Channel.setCurrent(channel); try { oos.writeObject(this); } finally { Channel.setCurrent(old); } } static Command readFrom(Channel channel, ObjectInputStream ois) throws IOException, ClassNotFoundException { Channel old = Channel.setCurrent(channel); try { return (Command)ois.readObject(); } finally { Channel.setCurrent(old); } } private static final long serialVersionUID = 1L; private final class Source extends Exception { public Source() { } private Source(Throwable cause) { super(cause); } public String toString() { return "Command "+Command.this.toString()+" created at"; } private static final long serialVersionUID = 1L; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/ExportTable.java0000644000175000017500000001522412122627370026257 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.ArrayList; /** * Manages unique ID for exported objects, and allows look-up from IDs. * * @author Kohsuke Kawaguchi */ final class ExportTable { private final Map table = new HashMap(); private final Map reverse = new HashMap(); /** * {@link ExportList}s which are actively recording the current * export operation. */ private final ThreadLocal lists = new ThreadLocal(); /** * Information about one exporetd object. */ private final class Entry { final int id; final T object; /** * Where was this object first exported? */ final Exception allocationTrace; /** * Current reference count. * Access to {@link ExportTable} is guarded by synchronized block, * so accessing this field requires no further synchronization. */ private int referenceCount; Entry(T object) { this.id = iota++; this.object = object; this.allocationTrace = new Exception(); // force the computation of the stack trace in a Java friendly data structure, // so that the call stack can be seen from the heap dump after the fact. allocationTrace.getStackTrace(); table.put(id,this); reverse.put(object,this); } void addRef() { referenceCount++; } /** * Increase reference count so much to effectively prevent de-allocation. * If the reference counting is correct, we just need to increment by one, * but this makes it safer even in case of some reference counting errors * (and we can still detect the problem by comparing the reference count with the magic value. */ void pin() { referenceCount += Integer.MAX_VALUE/2; } void release() { if(--referenceCount==0) { table.remove(id); reverse.remove(object); } } } /** * Captures the list of export, so that they can be unexported later. * * This is tied to a particular thread, so it only records operations * on the current thread. */ public final class ExportList extends ArrayList { private final ExportList old; private ExportList() { old=lists.get(); lists.set(this); } void release() { synchronized(ExportTable.this) { for (Entry e : this) e.release(); } } void stopRecording() { lists.set(old); } } /** * Unique ID generator. */ private int iota = 1; /** * Starts the recording of the export operations * and returns the list that captures the result. * * @see ExportList#stopRecording() */ public ExportList startRecording() { ExportList el = new ExportList(); lists.set(el); return el; } public boolean isRecording() { return lists.get()!=null; } /** * Exports the given object. * *

* Until the object is {@link #unexport(Object) unexported}, it will * not be subject to GC. * * @return * The assigned 'object ID'. If the object is already exported, * it will return the ID already assigned to it. */ public synchronized int export(T t) { return export(t,true); } /** * @param notifyListener * If false, listener will not be notified. This is used to * create an export that won't get unexported when the call returns. */ public synchronized int export(T t, boolean notifyListener) { if(t==null) return 0; // bootstrap classloader Entry e = reverse.get(t); if(e==null) e = new Entry(t); e.addRef(); if(notifyListener) { ExportList l = lists.get(); if(l!=null) l.add(e); } return e.id; } /*package*/ synchronized void pin(T t) { Entry e = reverse.get(t); if(e==null) e = new Entry(t); e.pin(); } public synchronized T get(int id) { Entry e = table.get(id); if(e!=null) return e.object; else return null; } /** * Removes the exported object from the table. */ public synchronized void unexport(T t) { if(t==null) return; Entry e = reverse.get(t); if(e==null) return; // presumably already unexported e.release(); } /** * Removes the exported object for the specified oid from the table. */ public synchronized void unexportByOid(Integer oid) { if(oid==null) return; Entry e = table.get(oid); if(e==null) return; // presumably already unexported e.release(); } /** * Dumps the contents of the table to a file. */ public synchronized void dump(PrintWriter w) throws IOException { for (Entry e : table.values()) { w.printf("#%d (ref.%d) : %s\n", e.id, e.referenceCount, e.object); e.allocationTrace.printStackTrace(w); } } /*package*/ synchronized boolean isExported(T o) { return reverse.containsKey(o); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/RequestAbortedException.java0000644000175000017500000000275612122627370030644 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * Signals that the communication is aborted and thus * the pending {@link Request} will never recover its {@link Response}. * * @author Kohsuke Kawaguchi */ public class RequestAbortedException extends RuntimeException { public RequestAbortedException(Throwable cause) { super(cause); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/ObjectInputStreamEx.java0000644000175000017500000000642412122627370027727 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; /** * {@link ObjectInputStream} that uses a specific class loader. */ public class ObjectInputStreamEx extends ObjectInputStream { private final ClassLoader cl; public ObjectInputStreamEx(InputStream in, ClassLoader cl) throws IOException { super(in); this.cl = cl; } @Override protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); try { return Class.forName(name, false, cl); } catch (ClassNotFoundException ex) { return super.resolveClass(desc); } } @Override protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { ClassLoader latestLoader = cl; ClassLoader nonPublicLoader = null; boolean hasNonPublicInterface = false; // define proxy in class loader of non-public interface(s), if any Class[] classObjs = new Class[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { Class cl = Class.forName(interfaces[i], false, latestLoader); if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { if (hasNonPublicInterface) { if (nonPublicLoader != cl.getClassLoader()) { throw new IllegalAccessError( "conflicting non-public interface class loaders"); } } else { nonPublicLoader = cl.getClassLoader(); hasNonPublicInterface = true; } } classObjs[i] = cl; } try { return Proxy.getProxyClass( hasNonPublicInterface ? nonPublicLoader : latestLoader, classObjs); } catch (IllegalArgumentException e) { throw new ClassNotFoundException(null, e); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/Response.java0000644000175000017500000000555412122627370025631 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * Request/response pattern over {@link Command}. * * This is layer 1. * * @author Kohsuke Kawaguchi * @see Request */ final class Response extends Command { /** * ID of the {@link Request} for which */ private final int id; /** * Set by the sender to the ID of the last I/O issued during the command execution. * The receiver will ensure that this I/O operation has completed before carrying out the task. * *

* If the sender doesn't support this, the receiver will see 0. * * @see PipeWriter */ private int lastIoId; final RSP returnValue; final EXC exception; Response(int id, int lastIoId, RSP returnValue) { this.id = id; this.lastIoId = lastIoId; this.returnValue = returnValue; this.exception = null; } Response(int id, int lastIoId, EXC exception) { this.id = id; this.lastIoId = lastIoId; this.returnValue = null; this.exception = exception; } /** * Notifies the waiting {@link Request}. */ @Override protected void execute(Channel channel) { Request req = channel.pendingCalls.get(id); if(req==null) return; // maybe aborted req.responseIoId = lastIoId; req.onCompleted(this); channel.pendingCalls.remove(id); } public String toString() { return "Response[retVal="+toString(returnValue)+",exception="+toString(exception)+"]"; } private static String toString(Object o) { if(o==null) return "null"; else return o.toString(); } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/ProxyInputStream.java0000644000175000017500000001153212122627370027341 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; /** * {@link InputStream} that reads bits from an exported * {@link InputStream} on a remote machine. * *

* TODO: pre-fetch bytes in advance * * @author Kohsuke Kawaguchi */ final class ProxyInputStream extends InputStream { private Channel channel; private int oid; /** * Creates an already connected {@link ProxyOutputStream}. * * @param oid * The object id of the exported {@link OutputStream}. */ public ProxyInputStream(Channel channel, int oid) throws IOException { this.channel = channel; this.oid = oid; } @Override public int read() throws IOException { try { Buffer buf = _read(1); if(buf.len==1) // byte->int expansion needs to be done carefully becaue byte in Java is signed // whose idea was it to make byte signed, anyway!? return ((int)buf.buf[0])&0xFF; else return -1; } catch (InterruptedException e) { // pretend EOF Thread.currentThread().interrupt(); // process interrupt later close(); return -1; } } private synchronized Buffer _read(int len) throws IOException, InterruptedException { return new Chunk(oid, len).call(channel); } @Override public int read(byte b[], int off, int len) throws IOException { try { Buffer buf = _read(len); if(buf.len==-1) return -1; System.arraycopy(buf.buf,0,b,off,buf.len); return buf.len; } catch (InterruptedException e) { // pretend EOF Thread.currentThread().interrupt(); // process interrupt later close(); return -1; } } @Override public synchronized void close() throws IOException { if(channel!=null) { channel.send(new EOF(oid)); channel = null; oid = -1; } } private static final class Buffer implements Serializable { byte[] buf; int len; public Buffer(int len) { this.buf = new byte[len]; } public void read(InputStream in) throws IOException { len = in.read(buf,0,buf.length); } private static final long serialVersionUID = 1L; } /** * Command to fetch bytes. */ private static final class Chunk extends Request { private final int oid; private final int len; public Chunk(int oid, int len) { this.oid = oid; this.len = len; } protected Buffer perform(Channel channel) throws IOException { InputStream in = (InputStream) channel.getExportedObject(oid); Buffer buf = new Buffer(len); buf.read(in); return buf; } private static final long serialVersionUID = 1L; } /** * {@link Command} for sending EOF. */ private static final class EOF extends Command { private final int oid; public EOF(int oid) { this.oid = oid; } protected void execute(Channel channel) { InputStream in = (InputStream) channel.getExportedObject(oid); channel.unexport(oid); try { in.close(); } catch (IOException e) { // ignore errors } } public String toString() { return "EOF("+oid+")"; } private static final long serialVersionUID = 1L; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/TeeOutputStream.java0000644000175000017500000000550112122627370027135 0ustar jamespagejamespage/* * 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 hudson.remoting; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Classic splitter of OutputStream. Named after the unix 'tee' * command. It allows a stream to be branched off so there * are now two streams. * * @version $Id: TeeOutputStream.java 610010 2008-01-08 14:50:59Z niallp $ */ class TeeOutputStream extends FilterOutputStream { /** the second OutputStream to write to */ protected OutputStream branch; /** * Constructs a TeeOutputStream. * @param out the main OutputStream * @param branch the second OutputStream */ public TeeOutputStream( OutputStream out, OutputStream branch ) { super(out); this.branch = branch; } /** * Write the bytes to both streams. * @param b the bytes to write * @throws IOException if an I/O error occurs */ public synchronized void write(byte[] b) throws IOException { super.write(b); this.branch.write(b); } /** * Write the specified bytes to both streams. * @param b the bytes to write * @param off The start offset * @param len The number of bytes to write * @throws IOException if an I/O error occurs */ public synchronized void write(byte[] b, int off, int len) throws IOException { super.write(b, off, len); this.branch.write(b, off, len); } /** * Write a byte to both streams. * @param b the byte to write * @throws IOException if an I/O error occurs */ public synchronized void write(int b) throws IOException { super.write(b); this.branch.write(b); } /** * Flushes both streams. * @throws IOException if an I/O error occurs */ public void flush() throws IOException { super.flush(); this.branch.flush(); } /** * Closes both streams. * @throws IOException if an I/O error occurs */ public void close() throws IOException { super.close(); this.branch.close(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/ClassicCommandTransport.java0000644000175000017500000001310612122627370030620 0ustar jamespagejamespagepackage hudson.remoting; import hudson.remoting.Channel.Mode; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; /** * The default {@link CommandTransport} that has been used historically. * *

* This implementation builds a {@link SynchronousCommandTransport} on top of a plain bi-directional byte stream. * {@link Mode} support allows this to be built on 8-bit unsafe transport, such as telnet. * * @author Kohsuke Kawaguchi * @since 2.13 */ /*package*/ final class ClassicCommandTransport extends SynchronousCommandTransport { private final ObjectInputStream ois; private final ObjectOutputStream oos; private final Capability remoteCapability; private final OutputStream underlyingStream; private ClassicCommandTransport(ObjectInputStream ois, ObjectOutputStream oos, OutputStream underlyingStream, Capability remoteCapability) { this.ois = ois; this.oos = oos; this.underlyingStream = underlyingStream; this.remoteCapability = remoteCapability; } @Override public Capability getRemoteCapability() throws IOException { return remoteCapability; } public final void write(Command cmd, boolean last) throws IOException { cmd.writeTo(channel,oos); oos.flush(); // make sure the command reaches the other end. // unless this is the last command, have OOS and remote OIS forget all the objects we sent // in this command. Otherwise it'll keep objects in memory unnecessarily. // However, this may fail if the command was the close, because that's supposed to be the last command // ever sent. It is possible for our ReaderThread to receive the reflecting close call from the other side // and close the output before the sending code gets to here. // See the comment from jglick on JENKINS-3077 about what happens if we do oos.reset(). if(!last) oos.reset(); } public void closeWrite() throws IOException { oos.close(); } public final Command read() throws IOException, ClassNotFoundException { return Command.readFrom(channel,ois); } public void closeRead() throws IOException { ois.close(); } @Override OutputStream getUnderlyingStream() { return underlyingStream; } public static CommandTransport create(Mode mode, InputStream is, OutputStream os, OutputStream header, ClassLoader base, Capability capability) throws IOException { if (base==null) base = ClassicCommandTransport.class.getClassLoader(); // write the magic preamble. // certain communication channel, such as forking JVM via ssh, // may produce some garbage at the beginning (for example a remote machine // might print some warning before the program starts outputting its own data.) // // so use magic preamble and discard all the data up to that to improve robustness. capability.writePreamble(os); ObjectOutputStream oos = null; if(mode!= Mode.NEGOTIATE) { os.write(mode.preamble); oos = new ObjectOutputStream(mode.wrap(os)); oos.flush(); // make sure that stream preamble is sent to the other end. avoids dead-lock } {// read the input until we hit preamble Mode[] modes={Mode.BINARY,Mode.TEXT}; byte[][] preambles = new byte[][]{Mode.BINARY.preamble, Mode.TEXT.preamble, Capability.PREAMBLE}; int[] ptr=new int[3]; Capability cap = new Capability(0); // remote capacity that we obtained. If we don't hear from remote, assume no capability while(true) { int ch = is.read(); if(ch==-1) throw new EOFException("unexpected stream termination"); for(int i=0;iUsage *

* To have a remote machine write to a local {@link OutputStream}: *

 * final OutputStream out = new RemoteOutputStream(os);
 *
 * channel.call(new Callable() {
 *   public Object call() {
 *     // this will write to 'os'.
 *     out.write(...);
 *   }
 * });
 * 
* *

* To have a local machine write to a remote {@link OutputStream}: * *

 * OutputStream os = channel.call(new Callable() {
 *   public Object call() {
 *       OutputStream os = new FileOutputStream(...); // or any other OutputStream
 *       return new RemoteOutputStream(os);
 *   }
 * });
 * 
* * @see RemoteInputStream * @author Kohsuke Kawaguchi */ public final class RemoteOutputStream extends OutputStream implements Serializable { /** * On local machine, this points to the {@link OutputStream} where * the data will be sent ultimately. * * On remote machine, this points to {@link ProxyOutputStream} that * does the network proxy. */ private transient OutputStream core; public RemoteOutputStream(OutputStream core) { if(core==null) throw new IllegalArgumentException(); this.core = core; } private void writeObject(ObjectOutputStream oos) throws IOException { int id = Channel.current().export(core,false); // this export is unexported in ProxyOutputStream.finalize() oos.writeInt(id); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { final Channel channel = Channel.current(); assert channel !=null; this.core = new ProxyOutputStream(channel, ois.readInt()); } private static final long serialVersionUID = 1L; // // // delegation to core // // public void write(int b) throws IOException { core.write(b); } public void write(byte[] b) throws IOException { core.write(b); } public void write(byte[] b, int off, int len) throws IOException { core.write(b, off, len); } public void flush() throws IOException { core.flush(); } public void close() throws IOException { core.close(); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjenkins-remoting-2.23/src/main/java/hudson/remoting/AbstractSynchronousByteArrayCommandTransport.javajenkins-remoting-2.23/src/main/java/hudson/remoting/AbstractSynchronousByteArrayCommandTransport.jav0000644000175000017500000000333212122627370034737 0ustar jamespagejamespagepackage hudson.remoting; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; /** * {@link SynchronousCommandTransport} that works with {@code byte[]} instead of command object. * * This base class hides away some of the {@link Command} serialization details. One less thing * for transport implementers to worry about. * * @author Kohsuke Kawaguchi * @since 2.13 */ public abstract class AbstractSynchronousByteArrayCommandTransport extends SynchronousCommandTransport { /** * Read a byte[] from the underlying transport for the given channel. */ public abstract byte[] readBlock(Channel channel) throws IOException, ClassNotFoundException; /** * Writes a byte[] to the transport. * * The block boundary is significant. A transport needs to ensure that that the same byte[] is * read by the peer through {@link #readBlock(Channel)} (unlike TCP, where a single write can * be split into multiple read()s on the other side.) */ public abstract void writeBlock(Channel channel, byte[] payload) throws IOException; @Override public Command read() throws IOException, ClassNotFoundException { return Command.readFrom(channel,new ObjectInputStreamEx( new ByteArrayInputStream(readBlock(channel)), channel.baseClassLoader)); } @Override public void write(Command cmd, boolean last) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); cmd.writeTo(channel,oos); oos.close(); writeBlock(channel,baos.toByteArray()); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/MimicException.java0000644000175000017500000000230212122627370026734 0ustar jamespagejamespagepackage hudson.remoting; /** * Exception that prints like the specified exception. * * This is used to carry the diagnostic information to the other side of the channel * in situations where we cannot use class remoting. * * @author Kohsuke Kawaguchi * @see Capability#hasMimicException() */ class MimicException extends Exception { private final String className; MimicException(Throwable cause) { super(cause.getMessage()); className = cause.getClass().getName(); setStackTrace(cause.getStackTrace()); if (cause.getCause()!=null) initCause(new MimicException(cause.getCause())); } @Override public String toString() { String s = className; String message = getLocalizedMessage(); return (message != null) ? (s + ": " + message) : s; } public static Throwable make(Channel ch, Throwable cause) { if (cause == null) return null; // make sure the remoting layer of the other end supports this if (ch.remoteCapability.hasMimicException()) return new MimicException(cause); else return cause; } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/Base64.java0000644000175000017500000002517712122627370025062 0ustar jamespagejamespage/* * Copyright 1999-2002,2004 The Apache Software Foundation. * * Licensed 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 hudson.remoting; /** * This class provides encode/decode for RFC 2045 Base64 as * defined by RFC 2045, N. Freed and N. Borenstein. * RFC 2045: Multipurpose Internet Mail Extensions (MIME) * Part One: Format of Internet Message Bodies. Reference * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt * This class is used by XML Schema binary format validation * * This implementation does not encode/decode streaming * data. You need the data that you will encode/decode * already on a byte arrray. * * @xerces.internal * * @author Jeffrey Rodriguez * @author Sandy Gao * @version $Id: Base64.java,v 1.4 2007/07/19 04:38:32 ofung Exp $ */ public final class Base64 { static private final int BASELENGTH = 128; static private final int LOOKUPLENGTH = 64; static private final int TWENTYFOURBITGROUP = 24; static private final int EIGHTBIT = 8; static private final int SIXTEENBIT = 16; static private final int SIXBIT = 6; static private final int FOURBYTE = 4; static private final int SIGN = -128; static private final char PAD = '='; static private final boolean fDebug = false; static final private byte [] base64Alphabet = new byte[BASELENGTH]; static final private char [] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; static { for (int i = 0; i < BASELENGTH; ++i) { base64Alphabet[i] = -1; } for (int i = 'Z'; i >= 'A'; i--) { base64Alphabet[i] = (byte) (i-'A'); } for (int i = 'z'; i>= 'a'; i--) { base64Alphabet[i] = (byte) ( i-'a' + 26); } for (int i = '9'; i >= '0'; i--) { base64Alphabet[i] = (byte) (i-'0' + 52); } base64Alphabet['+'] = 62; base64Alphabet['/'] = 63; for (int i = 0; i<=25; i++) lookUpBase64Alphabet[i] = (char)('A'+i); for (int i = 26, j = 0; i<=51; i++, j++) lookUpBase64Alphabet[i] = (char)('a'+ j); for (int i = 52, j = 0; i<=61; i++, j++) lookUpBase64Alphabet[i] = (char)('0' + j); lookUpBase64Alphabet[62] = (char)'+'; lookUpBase64Alphabet[63] = (char)'/'; } protected static boolean isWhiteSpace(char octect) { return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); } protected static boolean isPad(char octect) { return (octect == PAD); } protected static boolean isData(char octect) { return (octect < BASELENGTH && base64Alphabet[octect] != -1); } protected static boolean isBase64(char octect) { return (isWhiteSpace(octect) || isPad(octect) || isData(octect)); } /** * Encodes hex octects into Base64 * * @param binaryData Array containing binaryData * @return Encoded Base64 array */ public static String encode(byte[] binaryData) { if (binaryData == null) return null; int lengthDataBits = binaryData.length*EIGHTBIT; if (lengthDataBits == 0) { return ""; } int fewerThan24bits = lengthDataBits%TWENTYFOURBITGROUP; int numberTriplets = lengthDataBits/TWENTYFOURBITGROUP; int numberQuartet = fewerThan24bits != 0 ? numberTriplets+1 : numberTriplets; char encodedData[] = null; encodedData = new char[numberQuartet*4]; byte k=0, l=0, b1=0,b2=0,b3=0; int encodedIndex = 0; int dataIndex = 0; if (fDebug) { System.out.println("number of triplets = " + numberTriplets ); } for (int i=0; i>4 ) ; decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) ); decodedData[encodedIndex++] = (byte)( b3<<6 | b4 ); } if (!isData( (d1 = base64Data[dataIndex++]) ) || !isData( (d2 = base64Data[dataIndex++]) )) { return null;//if found "no data" just return null } b1 = base64Alphabet[d1]; b2 = base64Alphabet[d2]; d3 = base64Data[dataIndex++]; d4 = base64Data[dataIndex++]; if (!isData( (d3 ) ) || !isData( (d4 ) )) {//Check if they are PAD characters if (isPad( d3 ) && isPad( d4)) { //Two PAD e.g. 3c[Pad][Pad] if ((b2 & 0xf) != 0)//last 4 bits should be zero return null; byte[] tmp = new byte[ i*3 + 1 ]; System.arraycopy( decodedData, 0, tmp, 0, i*3 ); tmp[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ; return tmp; } else if (!isPad( d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad] b3 = base64Alphabet[ d3 ]; if ((b3 & 0x3 ) != 0)//last 2 bits should be zero return null; byte[] tmp = new byte[ i*3 + 2 ]; System.arraycopy( decodedData, 0, tmp, 0, i*3 ); tmp[encodedIndex++] = (byte)( b1 <<2 | b2>>4 ); tmp[encodedIndex] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) ); return tmp; } else { return null;//an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data } } else { //No PAD e.g 3cQl b3 = base64Alphabet[ d3 ]; b4 = base64Alphabet[ d4 ]; decodedData[encodedIndex++] = (byte)( b1 <<2 | b2>>4 ) ; decodedData[encodedIndex++] = (byte)(((b2 & 0xf)<<4 ) |( (b3>>2) & 0xf) ); decodedData[encodedIndex++] = (byte)( b3<<6 | b4 ); } return decodedData; } /** * remove WhiteSpace from MIME containing encoded Base64 data. * * @param data the byte array of base64 data (with WS) * @return the new length */ protected static int removeWhiteSpace(char[] data) { if (data == null) return 0; // count characters that's not whitespace int newSize = 0; int len = data.length; for (int i = 0; i < len; i++) { if (!isWhiteSpace(data[i])) data[newSize++] = data[i]; } return newSize; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/CallableFilter.java0000644000175000017500000000103512122627370026666 0ustar jamespagejamespagepackage hudson.remoting; /** * Decorator on {@code Callable.call()} to filter the execution. * * @author Kohsuke Kawaguchi */ public interface CallableFilter { /** * This implementation should normally look something like this: * *
     * V call(Callable c) {
     *     doSomePrep();
     *     try {
     *         return c.call();
     *     } finally {
     *         doSomeCleanUp();
     *     }
     * }
     * 
*/ V call(java.util.concurrent.Callable callable) throws Exception; } jenkins-remoting-2.23/src/main/java/hudson/remoting/Engine.java0000644000175000017500000003244412122627370025236 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ByteArrayOutputStream; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URL; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.List; import java.util.Collections; import java.util.logging.Logger; /** * Slave agent engine that proactively connects to Hudson master. * * @author Kohsuke Kawaguchi */ public class Engine extends Thread { /** * Thread pool that sets {@link #CURRENT}. */ private final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() { private final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); public Thread newThread(final Runnable r) { return defaultFactory.newThread(new Runnable() { public void run() { CURRENT.set(Engine.this); r.run(); } }); } }); public final EngineListener listener; /** * To make Hudson more graceful against user error, * JNLP agent can try to connect to multiple possible Hudson URLs. * This field specifies those candidate URLs, such as * "http://foo.bar/hudson/". */ private List candidateUrls; /** * URL that points to Hudson's tcp slage agent listener, like http://myhost/hudson/ * *

* This value is determined from {@link #candidateUrls} after a successful connection. * Note that this URL DOES NOT have "tcpSlaveAgentListener" in it. */ private URL hudsonUrl; private final String secretKey; public final String slaveName; private String credentials; /** * See Main#tunnel in the jnlp-agent module for the details. */ private String tunnel; private boolean noReconnect; /** * This cookie identifiesof the current connection, allowing us to force the server to drop * the client if we initiate a reconnection from our end (even when the server still thinks * the connection is alive.) */ private String cookie; public Engine(EngineListener listener, List hudsonUrls, String secretKey, String slaveName) { this.listener = listener; this.candidateUrls = hudsonUrls; this.secretKey = secretKey; this.slaveName = slaveName; if(candidateUrls.isEmpty()) throw new IllegalArgumentException("No URLs given"); } public URL getHudsonUrl() { return hudsonUrl; } public void setTunnel(String tunnel) { this.tunnel = tunnel; } public void setCredentials(String creds) { this.credentials = creds; } public void setNoReconnect(boolean noReconnect) { this.noReconnect = noReconnect; } @SuppressWarnings({"ThrowableInstanceNeverThrown"}) @Override public void run() { try { boolean first = true; while(true) { if(first) { first = false; } else { if(noReconnect) return; // exit } listener.status("Locating server among " + candidateUrls); Throwable firstError=null; String port=null; for (URL url : candidateUrls) { String s = url.toExternalForm(); if(!s.endsWith("/")) s+='/'; URL salURL = new URL(s+"tcpSlaveAgentListener/"); // find out the TCP port HttpURLConnection con = (HttpURLConnection)salURL.openConnection(); if (con instanceof HttpURLConnection && credentials != null) { // XXX /tcpSlaveAgentListener is unprotected so why do we need to pass any credentials? String encoding = Base64.encode(credentials.getBytes()); con.setRequestProperty("Authorization", "Basic " + encoding); } try { try { con.setConnectTimeout(30000); con.setReadTimeout(60000); con.connect(); } catch (IOException x) { if (firstError == null) { firstError = new IOException("Failed to connect to " + salURL + ": " + x.getMessage()).initCause(x); } continue; } port = con.getHeaderField("X-Hudson-JNLP-Port"); if(con.getResponseCode()!=200) { if(firstError==null) firstError = new Exception(salURL+" is invalid: "+con.getResponseCode()+" "+con.getResponseMessage()); continue; } if(port ==null) { if(firstError==null) firstError = new Exception(url+" is not Jenkins"); continue; } } finally { con.disconnect(); } // this URL works. From now on, only try this URL hudsonUrl = url; firstError = null; candidateUrls = Collections.singletonList(hudsonUrl); break; } if(firstError!=null) { listener.error(firstError); return; } Socket s = connect(port); listener.status("Handshaking"); DataOutputStream dos = new DataOutputStream(s.getOutputStream()); BufferedInputStream in = new BufferedInputStream(s.getInputStream()); dos.writeUTF("Protocol:JNLP2-connect"); Properties props = new Properties(); props.put("Secret-Key", secretKey); props.put("Node-Name", slaveName); if (cookie!=null) props.put("Cookie", cookie); ByteArrayOutputStream o = new ByteArrayOutputStream(); props.store(o, null); dos.writeUTF(o.toString("UTF-8")); String greeting = readLine(in); if (greeting.startsWith("Unknown protocol")) { LOGGER.info("The server didn't understand the v2 handshake. Falling back to v1 handshake"); s.close(); s = connect(port); in = new BufferedInputStream(s.getInputStream()); dos = new DataOutputStream(s.getOutputStream()); dos.writeUTF("Protocol:JNLP-connect"); dos.writeUTF(secretKey); dos.writeUTF(slaveName); greeting = readLine(in); // why, oh why didn't I use DataOutputStream when writing to the network? if (!greeting.equals(GREETING_SUCCESS)) { onConnectionRejected(greeting); continue; } } else { if (greeting.equals(GREETING_SUCCESS)) { Properties responses = readResponseHeaders(in); cookie = responses.getProperty("Cookie"); } else { onConnectionRejected(greeting); continue; } } final Channel channel = new Channel("channel", executor, in, new BufferedOutputStream(s.getOutputStream())); listener.status("Connected"); channel.join(); listener.status("Terminated"); listener.onDisconnect(); if(noReconnect) return; // exit // try to connect back to the server every 10 secs. waitForServerToBack(); } } catch (Throwable e) { listener.error(e); } } private void onConnectionRejected(String greeting) throws InterruptedException { listener.error(new Exception("The server rejected the connection: "+greeting)); Thread.sleep(10*1000); } private Properties readResponseHeaders(BufferedInputStream in) throws IOException { Properties response = new Properties(); while (true) { String line = readLine(in); if (line.length()==0) return response; int idx = line.indexOf(':'); response.put(line.substring(0,idx).trim(), line.substring(idx+1).trim()); } } /** * Read until '\n' and returns it as a string. */ private static String readLine(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); while (true) { int ch = in.read(); if (ch<0 || ch=='\n') return baos.toString().trim(); // trim off possible '\r' baos.write(ch); } } /** * Connects to TCP slave port, with a few retries. */ private Socket connect(String port) throws IOException, InterruptedException { String host = this.hudsonUrl.getHost(); if(tunnel!=null) { String[] tokens = tunnel.split(":",3); if(tokens.length!=2) throw new IOException("Illegal tunneling parameter: "+tunnel); if(tokens[0].length()>0) host = tokens[0]; if(tokens[1].length()>0) port = tokens[1]; } String msg = "Connecting to " + host + ':' + port; listener.status(msg); int retry = 1; while(true) { try { Socket s = new Socket(host, Integer.parseInt(port)); s.setTcpNoDelay(true); // we'll do buffering by ourselves // set read time out to avoid infinite hang. the time out should be long enough so as not // to interfere with normal operation. the main purpose of this is that when the other peer dies // abruptly, we shouldn't hang forever, and at some point we should notice that the connection // is gone. s.setSoTimeout(30*60*1000); // 30 mins. See PingThread for the ping interval return s; } catch (IOException e) { if(retry++>10) throw (IOException)new IOException("Failed to connect to "+host+':'+port).initCause(e); Thread.sleep(1000*10); listener.status(msg+" (retrying:"+retry+")",e); } } } /** * Waits for the server to come back. */ private void waitForServerToBack() throws InterruptedException { while(true) { Thread.sleep(1000*10); try { // Hudson top page might be read-protected. see http://www.nabble.com/more-lenient-retry-logic-in-Engine.waitForServerToBack-td24703172.html HttpURLConnection con = (HttpURLConnection)new URL(hudsonUrl,"tcpSlaveAgentListener/").openConnection(); con.connect(); if(con.getResponseCode()==200) return; } catch (IOException e) { // retry } } } /** * When invoked from within remoted {@link Callable} (that is, * from the thread that carries out the remote requests), * this method returns the {@link Engine} in which the remote operations * run. */ public static Engine current() { return CURRENT.get(); } private static final ThreadLocal CURRENT = new ThreadLocal(); private static final Logger LOGGER = Logger.getLogger(Engine.class.getName()); public static final String GREETING_SUCCESS = "Welcome"; } jenkins-remoting-2.23/src/main/java/hudson/remoting/AsyncFutureImpl.java0000644000175000017500000000666312122627370027127 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.CancellationException; /** * {@link Future} implementation whose computation is carried out elsewhere. * * Call the {@link #set(Object)} method or {@link #set(Throwable)} method to set the value to the future. * * @author Kohsuke Kawaguchi */ public class AsyncFutureImpl implements Future { /** * Setting this field to true will indicate that the computation is completed. * *

* One of the following three fields also needs to be set at the same time. */ private boolean completed; private V value; private Throwable problem; private boolean cancelled; public AsyncFutureImpl() {} public AsyncFutureImpl(V value) { set(value); } public AsyncFutureImpl(Throwable value) { set(value); } public boolean cancel(boolean mayInterruptIfRunning) { return false; } public boolean isCancelled() { return cancelled; } public synchronized boolean isDone() { return completed; } public synchronized V get() throws InterruptedException, ExecutionException { while(!completed) wait(); if(problem!=null) throw new ExecutionException(problem); if(cancelled) throw new CancellationException(); return value; } public synchronized V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if(!completed) wait(unit.toMillis(timeout)); if(!completed) throw new TimeoutException(); if(cancelled) throw new CancellationException(); return get(); } public synchronized void set(V value) { completed = true; this.value = value; notifyAll(); } public synchronized void set(Throwable problem) { completed = true; this.problem = problem; notifyAll(); } /** * Marks this task as cancelled. */ public synchronized void setAsCancelled() { completed = true; cancelled = true; notifyAll(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/IChannel.java0000644000175000017500000000273712122627370025514 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * Internally used to mark methods on {@link Channel} that are exported to remote. * *

* Behaviors of the methods are explained in {@link Channel}. * * @author Kohsuke Kawaguchi */ interface IChannel { Object getProperty(Object key); Object waitForProperty(Object key) throws InterruptedException; } jenkins-remoting-2.23/src/main/java/hudson/remoting/ProxyWriter.java0000644000175000017500000001250612122627370026344 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.CharArrayWriter; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; /** * {@link Writer} that sends bits to an exported * {@link Writer} on a remote machine. */ final class ProxyWriter extends Writer { private Channel channel; private int oid; /** * If bytes are written to this stream before it's connected * to a remote object, bytes will be stored in this buffer. */ private CharArrayWriter tmp; /** * Set to true if the stream is closed. */ private boolean closed; /** * Creates unconnected {@link ProxyWriter}. * The returned stream accepts data right away, and * when it's {@link #connect(Channel,int) connected} later, * the data will be sent at once to the remote stream. */ public ProxyWriter() { } /** * Creates an already connected {@link ProxyWriter}. * * @param oid * The object id of the exported {@link OutputStream}. */ public ProxyWriter(Channel channel, int oid) throws IOException { connect(channel,oid); } /** * Connects this stream to the specified remote object. */ synchronized void connect(Channel channel, int oid) throws IOException { if(this.channel!=null) throw new IllegalStateException("Cannot connect twice"); this.channel = channel; this.oid = oid; // if we already have bytes to write, do so now. if(tmp!=null) { write(tmp.toCharArray()); tmp = null; } if(closed) // already marked closed? close(); } public void write(int c) throws IOException { write(new char[]{(char)c},0,1); } public void write(char[] cbuf, int off, int len) throws IOException { if(closed) throw new IOException("stream is already closed"); if(off==0 && len==cbuf.length) write(cbuf); else { char[] buf = new char[len]; System.arraycopy(cbuf,off,buf,0,len); write(buf); } } public synchronized void write(char[] cbuf) throws IOException { if(closed) throw new IOException("stream is already closed"); if(channel==null) { if(tmp==null) tmp = new CharArrayWriter(); tmp.write(cbuf); } else { channel.send(new Chunk(oid,cbuf)); } } public void flush() throws IOException { // noop } public synchronized void close() throws IOException { closed = true; if(channel!=null) { channel.send(new EOF(oid)); channel = null; oid = -1; } } protected void finalize() throws Throwable { super.finalize(); close(); } /** * {@link Command} for sending bytes. */ private static final class Chunk extends Command { private final int oid; private final char[] buf; public Chunk(int oid, char[] buf) { this.oid = oid; this.buf = buf; } protected void execute(Channel channel) { Writer os = (Writer) channel.getExportedObject(oid); try { os.write(buf); } catch (IOException e) { // ignore errors } } public String toString() { return "Pipe.Chunk("+oid+","+buf.length+")"; } private static final long serialVersionUID = 1L; } /** * {@link Command} for sending EOF. */ private static final class EOF extends Command { private final int oid; public EOF(int oid) { this.oid = oid; } protected void execute(Channel channel) { OutputStream os = (OutputStream) channel.getExportedObject(oid); channel.unexport(oid); try { os.close(); } catch (IOException e) { // ignore errors } } public String toString() { return "Pipe.EOF("+oid+")"; } private static final long serialVersionUID = 1L; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/LocalChannel.java0000644000175000017500000000664112122627370026354 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * {@link VirtualChannel} that performs computation on the local JVM. * * @author Kohsuke Kawaguchi */ public class LocalChannel implements VirtualChannel { private final ExecutorService executor; public LocalChannel(ExecutorService executor) { this.executor = executor; } public V call(Callable callable) throws T { return callable.call(); } public Future callAsync(final Callable callable) throws IOException { final java.util.concurrent.Future f = executor.submit(new java.util.concurrent.Callable() { public V call() throws Exception { try { return callable.call(); } catch (Exception t) { throw t; } catch (Error t) { throw t; } catch (Throwable t) { throw new ExecutionException(t); } } }); return new Future() { public boolean cancel(boolean mayInterruptIfRunning) { return f.cancel(mayInterruptIfRunning); } public boolean isCancelled() { return f.isCancelled(); } public boolean isDone() { return f.isDone(); } public V get() throws InterruptedException, ExecutionException { return f.get(); } public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return f.get(timeout,unit); } }; } public void close() { // noop } public void join() throws InterruptedException { // noop } public void join(long timeout) throws InterruptedException { // noop } public T export(Class intf, T instance) { return instance; } public void syncLocalIO() throws InterruptedException { // noop } } jenkins-remoting-2.23/src/main/java/hudson/remoting/Channel.java0000644000175000017500000013651712122627370025407 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import edu.umd.cs.findbugs.annotations.SuppressWarnings; import hudson.remoting.CommandTransport.CommandReceiver; import hudson.remoting.ExportTable.ExportList; import hudson.remoting.PipeWindow.Key; import hudson.remoting.PipeWindow.Real; import hudson.remoting.forward.ListeningPort; import hudson.remoting.forward.ForwarderFactory; import hudson.remoting.forward.PortForwarder; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.Hashtable; import java.util.Map; import java.util.Vector; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import java.net.URL; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; /** * Represents a communication channel to the remote peer. * *

* A {@link Channel} is a mechanism for two JVMs to communicate over * bi-directional {@link InputStream}/{@link OutputStream} pair. * {@link Channel} represents an endpoint of the stream, and thus * two {@link Channel}s are always used in a pair. * *

* Communication is established as soon as two {@link Channel} instances * are created at the end fo the stream pair * until the stream is terminated via {@link #close()}. * *

* The basic unit of remoting is an executable {@link Callable} object. * An application can create a {@link Callable} object, and execute it remotely * by using the {@link #call(Callable)} method or {@link #callAsync(Callable)} method. * *

* In this sense, {@link Channel} is a mechanism to delegate/offload computation * to other JVMs and somewhat like an agent system. This is bit different from * remoting technologies like CORBA or web services, where the server exposes a * certain functionality that clients invoke. * *

* {@link Callable} object, as well as the return value / exceptions, * are transported by using Java serialization. All the necessary class files * are also shipped over {@link Channel} on-demand, so there's no need to * pre-deploy such classes on both JVMs. * * *

Implementor's Note

*

* {@link Channel} builds its features in a layered model. Its higher-layer * features are built on top of its lower-layer features, and they * are called layer-0, layer-1, etc. * *

    *
  • * Layer 0: * See {@link Command} for more details. This is for higher-level features, * and not likely useful for applications directly. *
  • * Layer 1: * See {@link Request} for more details. This is for higher-level features, * and not likely useful for applications directly. *
* * @author Kohsuke Kawaguchi */ public class Channel implements VirtualChannel, IChannel, Closeable { private final CommandTransport transport; /** * {@link OutputStream} that's given to the constructor. This is the hand-off with the lower layer. * * @deprecated * See {@link #getUnderlyingOutput()}. */ private final OutputStream underlyingOutput; /** * Human readable description of where this channel is connected to. Used during diagnostic output * and error reports. */ private final String name; private volatile boolean isRestricted; /*package*/ final InterceptingExecutorService executor; /** * If non-null, the incoming link is already shut down, * and reader is already terminated. The {@link Throwable} object indicates why the outgoing channel * was closed. */ private volatile Throwable inClosed = null; /** * If non-null, the outgoing link is already shut down, * and no command can be sent. The {@link Throwable} object indicates why the outgoing channel * was closed. */ private volatile Throwable outClosed = null; /** * Requests that are sent to the remote side for execution, yet we are waiting locally until * we hear back their responses. */ /*package*/ final Map> pendingCalls = new Hashtable>(); /** * Remembers last I/O ID issued from locally to the other side, per thread. * int[1] is used as a holder of int. */ private final ThreadLocal lastIoId = new ThreadLocal() { @Override protected int[] initialValue() { return new int[1]; } }; /** * Records the {@link Request}s being executed on this channel, sent by the remote peer. */ /*package*/ final Map> executingCalls = Collections.synchronizedMap(new Hashtable>()); /** * {@link ClassLoader}s that are proxies of the remote classloaders. */ /*package*/ final ImportedClassLoaderTable importedClassLoaders = new ImportedClassLoaderTable(this); /** * Objects exported via {@link #export(Class, Object)}. */ /*package (for test)*/ final ExportTable exportedObjects = new ExportTable(); /** * {@link PipeWindow}s keyed by their OIDs (of the OutputStream exported by the other side.) * *

* To make the GC of {@link PipeWindow} automatic, the use of weak references here are tricky. * A strong reference to {@link PipeWindow} is kept from {@link ProxyOutputStream}, and * this is the only strong reference. Thus while {@link ProxyOutputStream} is alive, * it keeps {@link PipeWindow} referenced, which in turn keeps its {@link PipeWindow.Real#key} * referenced, hence this map can be looked up by the OID. When the {@link ProxyOutputStream} * will be gone, the key is no longer strongly referenced, so it'll get cleaned up. * *

* In some race condition situation, it might be possible for us to lose the tracking of the collect * window size. But as long as we can be sure that there's only one {@link PipeWindow} instance * per OID, it will only result in a temporary spike in the effective window size, * and therefore should be OK. */ private final WeakHashMap> pipeWindows = new WeakHashMap>(); /** * Registered listeners. */ private final Vector listeners = new Vector(); private int gcCounter; private int commandsSent; /** * Total number of nanoseconds spent for remote class loading. *

* Remote code execution often results in classloading activity * (more precisely, when the remote peer requests some computation * on this channel, this channel often has to load necessary * classes from the remote peer.) *

* This counter represents the total amount of time this channel * had to spend loading classes from the remote peer. The time * measurement doesn't include the time locally spent to actually * define the class (as the local classloading would have incurred * the same cost.) */ public final AtomicLong classLoadingTime = new AtomicLong(); /** * Total counts of remote classloading activities. Used in a pair * with {@link #classLoadingTime}. */ public final AtomicInteger classLoadingCount = new AtomicInteger(); /** * Total number of nanoseconds spent for remote resource loading. * @see #classLoadingTime */ public final AtomicLong resourceLoadingTime = new AtomicLong(); /** * Total count of remote resource loading. * @see #classLoadingCount */ public final AtomicInteger resourceLoadingCount = new AtomicInteger(); private final AtomicInteger ioId = new AtomicInteger(); /** * Property bag that contains application-specific stuff. */ private final Hashtable properties = new Hashtable(); /** * Proxy to the remote {@link Channel} object. */ private final IChannel remoteChannel; /** * Capability of the remote {@link Channel}. */ public final Capability remoteCapability; /** * When did we receive any data from this slave the last time? * This can be used as a basis for detecting dead connections. *

* Note that this doesn't include our sender side of the operation, * as successfully returning from {@link #send(Command)} doesn't mean * anything in terms of whether the underlying network was able to send * the data (for example, if the other end of a socket connection goes down * without telling us anything, the {@link SocketOutputStream#write(int)} will * return right away, and the socket only really times out after 10s of minutes. */ private volatile long lastHeard; /** * Single-thread executor for running pipe I/O operations. * * It is executed in a separate thread to avoid blocking the channel reader thread * in case read/write blocks. It is single thread to ensure FIFO; I/O needs to execute * in the same order the remote peer told us to execute them. */ /*package*/ final PipeWriter pipeWriter; /** * ClassLaoder that remote classloaders should use as the basis. */ /*package*/ final ClassLoader baseClassLoader; /** * Communication mode used in conjunction with {@link ClassicCommandTransport}. * * @since 1.161 */ public enum Mode { /** * Send binary data over the stream. Most efficient. */ BINARY(new byte[]{0,0,0,0}), /** * Send ASCII over the stream. Uses base64, so the efficiency goes down by 33%, * but this is useful where stream is binary-unsafe, such as telnet. */ TEXT("<===[HUDSON TRANSMISSION BEGINS]===>") { @Override protected OutputStream wrap(OutputStream os) { return BinarySafeStream.wrap(os); } @Override protected InputStream wrap(InputStream is) { return BinarySafeStream.wrap(is); } }, /** * Let the remote peer decide the transmission mode and follow that. * Note that if both ends use NEGOTIATE, it will dead lock. */ NEGOTIATE(new byte[0]); /** * Preamble used to indicate the tranmission mode. * Because of the algorithm we use to detect the preamble, * the string cannot be any random string. For example, * if the preamble is "AAB", we'll fail to find a preamble * in "AAAB". */ /*package*/ final byte[] preamble; Mode(String preamble) { try { this.preamble = preamble.getBytes("US-ASCII"); } catch (UnsupportedEncodingException e) { throw new Error(e); } } Mode(byte[] preamble) { this.preamble = preamble; } protected OutputStream wrap(OutputStream os) { return os; } protected InputStream wrap(InputStream is) { return is; } } public Channel(String name, ExecutorService exec, InputStream is, OutputStream os) throws IOException { this(name,exec,Mode.BINARY,is,os,null); } public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os) throws IOException { this(name,exec,mode,is,os,null); } public Channel(String name, ExecutorService exec, InputStream is, OutputStream os, OutputStream header) throws IOException { this(name,exec,Mode.BINARY,is,os,header); } public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header) throws IOException { this(name,exec,mode,is,os,header,false); } public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header, boolean restricted) throws IOException { this(name,exec,mode,is,os,header,restricted,null); } /** * Creates a new channel. * * @param name * Human readable name of this channel. Used for debug/logging. Can be anything. * @param exec * Commands sent from the remote peer will be executed by using this {@link Executor}. * @param mode * The encoding to be used over the stream. * @param is * Stream connected to the remote peer. It's the caller's responsibility to do * buffering on this stream, if that's necessary. * @param os * Stream connected to the remote peer. It's the caller's responsibility to do * buffering on this stream, if that's necessary. * @param header * If non-null, receive the portion of data in is before * the data goes into the "binary mode". This is useful * when the established communication channel might include some data that might * be useful for debugging/trouble-shooting. * @param base * Specify the classloader used for deserializing remote commands. * This is primarily related to {@link #getRemoteProperty(Object)}. Sometimes two parties * communicate over a channel and pass objects around as properties, but those types might not be * visible from the classloader loading the {@link Channel} class. In such a case, specify a classloader * so that those classes resolve. If null, {@code Channel.class.getClassLoader()} is used. * @param restricted * If true, this channel won't accept {@link Command}s that allow the remote end to execute arbitrary closures * --- instead they can only call methods on objects that are exported by this channel. * This also prevents the remote end from loading classes into JVM. * * Note that it still allows the remote end to deserialize arbitrary object graph * (provided that all the classes are already available in this JVM), so exactly how * safe the resulting behavior is is up to discussion. */ public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header, boolean restricted, ClassLoader base) throws IOException { this(name,exec,mode,is,os,header,restricted,base,new Capability()); } /*package*/ Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header, boolean restricted, ClassLoader base, Capability capability) throws IOException { this(name,exec, ClassicCommandTransport.create(mode, is, os, header, base, capability),restricted,base); } /** * Creates a new channel. * * @param name * See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, OutputStream, boolean, ClassLoader)} * @param exec * See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, OutputStream, boolean, ClassLoader)} * @param transport * The transport that we run {@link Channel} on top of. * @param base * See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, OutputStream, boolean, ClassLoader)} * @param restricted * See {@link #Channel(String, ExecutorService, Mode, InputStream, OutputStream, OutputStream, boolean, ClassLoader)} * @since 2.13 */ public Channel(String name, ExecutorService exec, CommandTransport transport, boolean restricted, ClassLoader base) throws IOException { this.name = name; this.executor = new InterceptingExecutorService(exec); this.isRestricted = restricted; this.underlyingOutput = transport.getUnderlyingStream(); if (base==null) base = getClass().getClassLoader(); this.baseClassLoader = base; if(export(this,false)!=1) throw new AssertionError(); // export number 1 is reserved for the channel itself remoteChannel = RemoteInvocationHandler.wrap(this,1,IChannel.class,true,false); this.remoteCapability = transport.getRemoteCapability(); this.pipeWriter = new PipeWriter(createPipeWriterExecutor()); this.transport = transport; transport.setup(this, new CommandReceiver() { public void handle(Command cmd) { lastHeard = System.currentTimeMillis(); if (logger.isLoggable(Level.FINE)) logger.fine("Received " + cmd); try { cmd.execute(Channel.this); } catch (Throwable t) { logger.log(Level.SEVERE, "Failed to execute command " + cmd + " (channel " + Channel.this.name + ")", t); logger.log(Level.SEVERE, "This command is created here", cmd.createdAt); } } public void terminate(IOException e) { Channel.this.terminate(e); } }); } /** * Callback "interface" for changes in the state of {@link Channel}. */ public static abstract class Listener { /** * When the channel was closed normally or abnormally due to an error. * * @param cause * if the channel is closed abnormally, this parameter * represents an exception that has triggered it. * Otherwise null. */ public void onClosed(Channel channel, IOException cause) {} } /*package*/ boolean isOutClosed() { return outClosed!=null; } /** * Creates the {@link ExecutorService} for writing to pipes. * *

* If the throttling is supported, use a separate thread to free up the main channel * reader thread (thus prevent blockage.) Otherwise let the channel reader thread do it, * which is the historical behaviour. */ private ExecutorService createPipeWriterExecutor() { if (remoteCapability.supportsPipeThrottling()) return Executors.newSingleThreadExecutor(new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(r,"Pipe writer thread: "+name); } }); return new SynchronousExecutorService(); } /** * Sends a command to the remote end and executes it there. * *

* This is the lowest layer of abstraction in {@link Channel}. * {@link Command}s are executed on a remote system in the order they are sent. */ /*package*/ synchronized void send(Command cmd) throws IOException { if(outClosed!=null) throw new ChannelClosedException(outClosed); if(logger.isLoggable(Level.FINE)) logger.fine("Send "+cmd); transport.write(cmd, cmd instanceof CloseCommand); commandsSent++; } /** * {@inheritDoc} */ public T export(Class type, T instance) { return export(type,instance,true); } /** * @param userProxy * If true, the returned proxy will be capable to handle classes * defined in the user classloader as parameters and return values. * Such proxy relies on {@link RemoteClassLoader} and related mechanism, * so it's not usable for implementing lower-layer services that are * used by {@link RemoteClassLoader}. * * To create proxies for objects inside remoting, pass in false. */ /*package*/ T export(Class type, T instance, boolean userProxy) { if(instance==null) return null; // every so often perform GC on the remote system so that // unused RemoteInvocationHandler get released, which triggers // unexport operation. if((++gcCounter)%10000==0) try { send(new GCCommand()); } catch (IOException e) { // for compatibility reason we can't change the export method signature logger.log(Level.WARNING, "Unable to send GC command",e); } // either local side will auto-unexport, or the remote side will unexport when it's GC-ed boolean autoUnexportByCaller = exportedObjects.isRecording(); final int id = export(instance,autoUnexportByCaller); return RemoteInvocationHandler.wrap(null, id, type, userProxy, autoUnexportByCaller); } /*package*/ int export(Object instance) { return exportedObjects.export(instance); } /*package*/ int export(Object instance, boolean automaticUnexport) { return exportedObjects.export(instance,automaticUnexport); } /*package*/ Object getExportedObject(int oid) { return exportedObjects.get(oid); } /*package*/ void unexport(int id) { exportedObjects.unexportByOid(id); } /** * Increase reference count so much to effectively prevent de-allocation. * * @see ExportTable.Entry#pin() */ public void pin(Object instance) { exportedObjects.pin(instance); } /** * {@linkplain #pin(Object) Pin down} the exported classloader. */ public void pinClassLoader(ClassLoader cl) { RemoteClassLoader.pin(cl,this); } /** * Preloads jar files on the remote side. * *

* This is a performance improvement method that can be safely * ignored if your goal is just to make things working. * *

* Normally, classes are transferred over the network one at a time, * on-demand. This design is mainly driven by how Java classloading works * — we can't predict what classes will be necessarily upfront very easily. * *

* Classes are loaded only once, so for long-running {@link Channel}, * this is normally an acceptable overhead. But sometimes, for example * when a channel is short-lived, or when you know that you'll need * a majority of classes in certain jar files, then it is more efficient * to send a whole jar file over the network upfront and thereby * avoiding individual class transfer over the network. * *

* That is what this method does. It ensures that a series of jar files * are copied to the remote side (AKA "preloading.") * Classloading will consult the preloaded jars before performing * network transfer of class files. * *

Beware that this method is not useful in all configurations. * If a {@link RemoteClassLoader} has another {@link RemoteClassLoader} as a * {@linkplain ClassLoader#getParent parent}, which would be typical, then preloading * a JAR in it will not reduce network round-trips: each class load still has to call * {@link ClassLoader#loadClass(String, boolean) loadClass} on the parent, which will * wind up checking the remote side just to get a negative answer. * * @param classLoaderRef * This parameter is used to identify the remote classloader * that will prefetch the specified jar files. That is, prefetching * will ensure that prefetched jars will kick in * when this {@link Callable} object is actually executed remote side. * *

* {@link RemoteClassLoader}s are created wisely, one per local {@link ClassLoader}, * so this parameter doesn't have to be exactly the same {@link Callable} * to be executed later — it just has to be of the same class. * @param classesInJar * {@link Class} objects that identify jar files to be preloaded. * Jar files that contain the specified classes will be preloaded into the remote peer. * You just need to specify one class per one jar. * @return * true if the preloading actually happened. false if all the jars * are already preloaded. This method is implemented in such a way that * unnecessary jar file transfer will be avoided, and the return value * will tell you if this optimization kicked in. Under normal circumstances * your program shouldn't depend on this return value. It's just a hint. * @throws IOException * if the preloading fails. */ public boolean preloadJar(Callable classLoaderRef, Class... classesInJar) throws IOException, InterruptedException { return preloadJar(UserRequest.getClassLoader(classLoaderRef),classesInJar); } public boolean preloadJar(ClassLoader local, Class... classesInJar) throws IOException, InterruptedException { URL[] jars = new URL[classesInJar.length]; for (int i = 0; i < classesInJar.length; i++) jars[i] = Which.jarFile(classesInJar[i]).toURI().toURL(); return call(new PreloadJarTask(jars,local)); } public boolean preloadJar(ClassLoader local, URL... jars) throws IOException, InterruptedException { return call(new PreloadJarTask(jars,local)); } /*package*/ PipeWindow getPipeWindow(int oid) { synchronized (pipeWindows) { Key k = new Key(oid); WeakReference v = pipeWindows.get(k); if (v!=null) { PipeWindow w = v.get(); if (w!=null) return w; } PipeWindow w; if (remoteCapability.supportsPipeThrottling()) w = new Real(k, PIPE_WINDOW_SIZE); else w = new PipeWindow.Fake(); pipeWindows.put(k,new WeakReference(w)); return w; } } /** * {@inheritDoc} */ public V call(Callable callable) throws IOException, T, InterruptedException { UserRequest request=null; try { request = new UserRequest(this, callable); UserResponse r = request.call(this); return r.retrieve(this, UserRequest.getClassLoader(callable)); // re-wrap the exception so that we can capture the stack trace of the caller. } catch (ClassNotFoundException e) { IOException x = new IOException("Remote call on "+name+" failed"); x.initCause(e); throw x; } catch (Error e) { IOException x = new IOException("Remote call on "+name+" failed"); x.initCause(e); throw x; } finally { // since this is synchronous operation, when the round trip is over // we assume all the exported objects are out of scope. // (that is, the operation shouldn't spawn a new thread or altter // global state in the remote system. if(request!=null) request.releaseExports(); } } /** * {@inheritDoc} */ public Future callAsync(final Callable callable) throws IOException { final Future> f = new UserRequest(this, callable).callAsync(this); return new FutureAdapter>(f) { protected V adapt(UserResponse r) throws ExecutionException { try { return r.retrieve(Channel.this, UserRequest.getClassLoader(callable)); } catch (Throwable t) {// really means catch(T t) throw new ExecutionException(t); } } }; } /** * Aborts the connection in response to an error. * * @param e * The error that caused the connection to be aborted. Never null. */ @java.lang.SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") @SuppressWarnings("ITA_INEFFICIENT_TO_ARRAY") // intentionally; race condition on listeners otherwise protected void terminate(IOException e) { try { synchronized (this) { if (e == null) throw new IllegalArgumentException(); outClosed = inClosed = e; try { transport.closeRead(); } catch (IOException x) { logger.log(Level.WARNING, "Failed to close down the reader side of the transport", x); } try { synchronized (pendingCalls) { for (Request req : pendingCalls.values()) req.abort(e); pendingCalls.clear(); } synchronized (executingCalls) { for (Request r : executingCalls.values()) { java.util.concurrent.Future f = r.future; if (f != null) f.cancel(true); } executingCalls.clear(); } } finally { notifyAll(); } } // JENKINS-14909: leave synch block } finally { if (e instanceof OrderlyShutdown) e = null; for (Listener l : listeners.toArray(new Listener[0])) l.onClosed(this, e); } } /** * Registers a new {@link Listener}. * * @see #removeListener(Listener) */ public void addListener(Listener l) { listeners.add(l); } /** * Removes a listener. * * @return * false if the given listener has not been registered to begin with. */ public boolean removeListener(Listener l) { return listeners.remove(l); } /** * Adds a {@link CallableFilter} that gets a chance to decorate every {@link Callable}s that run locally * sent by the other peer. * * This is useful to tweak the environment those closures are run, such as setting up the thread context * environment. */ public void addLocalExecutionInterceptor(CallableFilter filter) { executor.addFilter(filter); } /** * Rmoves the filter introduced by {@link #addLocalExecutionInterceptor(CallableFilter)}. */ public void removeLocalExecutionInterceptor(CallableFilter filter) { executor.removeFilter(filter); } /** * Waits for this {@link Channel} to be closed down. * * The close-down of a {@link Channel} might be initiated locally or remotely. * * @throws InterruptedException * If the current thread is interrupted while waiting for the completion. */ public synchronized void join() throws InterruptedException { while(inClosed==null || outClosed==null) wait(); } /** * If the receiving end of the channel is closed (that is, if we are guaranteed to receive nothing further), * this method returns true. */ /*package*/ boolean isInClosed() { return inClosed!=null; } /** * Returns true if this channel is currently does not load classes from the remote peer. */ public boolean isRestricted() { return isRestricted; } public void setRestricted(boolean b) { isRestricted = b; } /** * Waits for this {@link Channel} to be closed down, but only up the given milliseconds. * * @throws InterruptedException * If the current thread is interrupted while waiting for the completion. * @since 1.299 */ public synchronized void join(long timeout) throws InterruptedException { long start = System.currentTimeMillis(); while(System.currentTimeMillis()-start * This mechanism can be used for one side to discover contextual objects created by the other JVM * (as opposed to executing {@link Callable}, which cannot have any reference to the context * of the remote {@link Channel}. */ public Object getProperty(Object key) { return properties.get(key); } public T getProperty(ChannelProperty key) { return key.type.cast(getProperty((Object) key)); } /** * Works like {@link #getProperty(Object)} but wait until some value is set by someone. */ public Object waitForProperty(Object key) throws InterruptedException { synchronized (properties) { while(true) { Object v = properties.get(key); if(v!=null) return v; properties.wait(); } } } public T waitForProperty(ChannelProperty key) throws InterruptedException { return key.type.cast(waitForProperty((Object) key)); } /** * Sets the property value on this side of the channel. * * @see #getProperty(Object) */ public Object setProperty(Object key, Object value) { synchronized (properties) { Object old = value!=null ? properties.put(key, value) : properties.remove(key); properties.notifyAll(); return old; } } public T setProperty(ChannelProperty key, T value) { return key.type.cast(setProperty((Object) key, value)); } /** * Gets the property set on the remote peer. * * @return null * if the property of the said key isn't set. */ public Object getRemoteProperty(Object key) { return remoteChannel.getProperty(key); } public T getRemoteProperty(ChannelProperty key) { return key.type.cast(getRemoteProperty((Object) key)); } /** * Gets the property set on the remote peer. * This method blocks until the property is set by the remote peer. */ public Object waitForRemoteProperty(Object key) throws InterruptedException { return remoteChannel.waitForProperty(key); } public T waitForRemoteProperty(ChannelProperty key) throws InterruptedException { return key.type.cast(waitForRemoteProperty((Object) key)); } /** * Obtain the output stream passed to the constructor. * * @deprecated * Future version of the remoting module may add other modes of creating channel * that doesn't involve stream pair. Therefore, we aren't committing to this method. * This method isn't a part of the committed API of the channel class. * @return * While the current version always return a non-null value, the caller must not * make that assumption for the above reason. This method may return null in the future version * to indicate that the {@link Channel} is not sitting on top of a stream pair. */ public OutputStream getUnderlyingOutput() { return underlyingOutput; } /** * Starts a local to remote port forwarding (the equivalent of "ssh -L"). * * @param recvPort * The port on this local machine that we'll listen to. 0 to let * OS pick a random available port. If you specify 0, use * {@link ListeningPort#getPort()} to figure out the actual assigned port. * @param forwardHost * The remote host that the connection will be forwarded to. * Connection to this host will be made from the other JVM that * this {@link Channel} represents. * @param forwardPort * The remote port that the connection will be forwarded to. * @return */ public ListeningPort createLocalToRemotePortForwarding(int recvPort, String forwardHost, int forwardPort) throws IOException, InterruptedException { PortForwarder portForwarder = new PortForwarder(recvPort, ForwarderFactory.create(this, forwardHost, forwardPort)); portForwarder.start(); return portForwarder; } /** * Starts a remote to local port forwarding (the equivalent of "ssh -R"). * * @param recvPort * The port on the remote JVM (represented by this {@link Channel}) * that we'll listen to. 0 to let * OS pick a random available port. If you specify 0, use * {@link ListeningPort#getPort()} to figure out the actual assigned port. * @param forwardHost * The remote host that the connection will be forwarded to. * Connection to this host will be made from this JVM. * @param forwardPort * The remote port that the connection will be forwarded to. * @return */ public ListeningPort createRemoteToLocalPortForwarding(int recvPort, String forwardHost, int forwardPort) throws IOException, InterruptedException { return PortForwarder.create(this,recvPort, ForwarderFactory.create(forwardHost, forwardPort)); } /** * Dispenses the unique I/O ID. * * When a {@link Channel} requests an activity that happens in {@link #pipeWriter}, * the sender assigns unique I/O ID to this request, which enables later * commands to sync up with their executions. * * @see PipeWriter */ /*package*/ int newIoId() { int v = ioId.incrementAndGet(); lastIoId.get()[0] = v; return v; } /** * Gets the last I/O ID issued by the calling thread, or 0 if none is recorded. */ /*package*/ int lastIoId() { return lastIoId.get()[0]; } /** * Blocks until all the I/O packets sent before this gets fully executed by the remote side, then return. * * @throws IOException * If the remote doesn't support this operation, or if sync fails for other reasons. */ public void syncIO() throws IOException, InterruptedException { call(new IOSyncer()); } // Barrier doesn't work because IOSyncer is a Callable and not Command // (yet making it Command would break JENKINS-5977, which introduced this split in the first place!) // /** // * Non-blocking version of {@link #syncIO()} that has a weaker commitment. // * // * This method only guarantees that any later remote commands will happen after all the I/O packets sent before // * this method call gets fully executed. This is faster in that it it doesn't wait for a response // * from the other side, yet it normally achieves the desired semantics. // */ // public void barrierIO() throws IOException { // callAsync(new IOSyncer()); // } public void syncLocalIO() throws InterruptedException { Thread t = Thread.currentThread(); String old = t.getName(); t.setName("I/O sync: "+old); try { // no one waits for the completion of this Runnable, so not using I/O ID pipeWriter.submit(0,new Runnable() { public void run() { // noop } }).get(); } catch (ExecutionException e) { throw new AssertionError(e); // impossible } finally { t.setName(old); } } private static final class IOSyncer implements Callable { public Object call() throws InterruptedException { Channel.current().syncLocalIO(); return null; } private static final long serialVersionUID = 1L; } public String getName() { return name; } @Override public String toString() { return super.toString()+":"+name; } /** * Dumps the list of exported objects and their allocation traces to the given output. */ public void dumpExportTable(PrintWriter w) throws IOException { exportedObjects.dump(w); } public ExportList startExportRecording() { return exportedObjects.startRecording(); } /** * @see #lastHeard */ public long getLastHeard() { return lastHeard; } /*package*/ static Channel setCurrent(Channel channel) { Channel old = CURRENT.get(); CURRENT.set(channel); return old; } /** * This method can be invoked during the serialization/deserialization of * objects when they are transferred to the remote {@link Channel}, * as well as during {@link Callable#call()} is invoked. * * @return null * if the calling thread is not performing serialization. */ public static Channel current() { return CURRENT.get(); } /** * Remembers the current "channel" associated for this thread. */ private static final ThreadLocal CURRENT = new ThreadLocal(); private static final Logger logger = Logger.getLogger(Channel.class.getName()); /** * Default pipe window size. * *

* This controls the amount of bytes that can be in flight. Value too small would fail to efficiently utilize * a high-latency/large-bandwidth network, but a value too large would cause the risk of a large memory consumption * when a pipe clogs (that is, the receiver isn't consuming bytes we are sending fast enough.) * *

* If we have a gigabit ethernet (with effective transfer rate of 100M bps) and 20ms latency, the pipe will hold * (100M bits/sec * 0.02sec / 8 bits/byte = 0.25MB. So 1MB or so is big enough for most network, and hopefully * this is an acceptable enough memory consumption in case of clogging. * * @see PipeWindow */ public static final int PIPE_WINDOW_SIZE = Integer.getInteger(Channel.class.getName()+".pipeWindowSize",1024*1024); // static { // ConsoleHandler h = new ConsoleHandler(); // h.setFormatter(new Formatter(){ // public synchronized String format(LogRecord record) { // StringBuilder sb = new StringBuilder(); // sb.append((record.getMillis()%100000)+100000); // sb.append(" "); // if (record.getSourceClassName() != null) { // sb.append(record.getSourceClassName()); // } else { // sb.append(record.getLoggerName()); // } // if (record.getSourceMethodName() != null) { // sb.append(" "); // sb.append(record.getSourceMethodName()); // } // sb.append('\n'); // String message = formatMessage(record); // sb.append(record.getLevel().getLocalizedName()); // sb.append(": "); // sb.append(message); // sb.append('\n'); // if (record.getThrown() != null) { // try { // StringWriter sw = new StringWriter(); // PrintWriter pw = new PrintWriter(sw); // record.getThrown().printStackTrace(pw); // pw.close(); // sb.append(sw.toString()); // } catch (Exception ex) { // } // } // return sb.toString(); // } // }); // h.setLevel(Level.FINE); // logger.addHandler(h); // logger.setLevel(Level.FINE); // } } jenkins-remoting-2.23/src/main/java/hudson/remoting/ImportedClassLoaderTable.java0000644000175000017500000000451012122627370030672 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.RemoteClassLoader.IClassLoader; import java.util.Hashtable; import java.util.Map; /** * @author Kohsuke Kawaguchi */ final class ImportedClassLoaderTable { final Channel channel; final Map classLoaders = new Hashtable(); ImportedClassLoaderTable(Channel channel) { this.channel = channel; } /** * Maps the exported object ID to the classloader. * *

* This method "consumes" the given oid for the purpose of reference counting. */ public synchronized ClassLoader get(int oid) { return get(RemoteInvocationHandler.wrap(channel,oid,IClassLoader.class,false,false)); } public synchronized ClassLoader get(IClassLoader classLoaderProxy) { ClassLoader r = classLoaders.get(classLoaderProxy); if(r==null) { // we need to be able to use the same hudson.remoting classes, hence delegate // to this class loader. r = RemoteClassLoader.create(channel.baseClassLoader,classLoaderProxy); classLoaders.put(classLoaderProxy,r); } return r; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/VirtualChannel.java0000644000175000017500000001000112122627370026731 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; /** * Virtualized {@link Channel} that allows different implementations. * * @author Kohsuke Kawaguchi */ public interface VirtualChannel { /** * Makes a remote procedure call. * *

* Sends {@link Callable} to the remote system, executes it, and returns its result. * * @throws InterruptedException * If the current thread is interrupted while waiting for the completion. * @throws IOException * If there's any error in the communication between {@link Channel}s. */ V call(Callable callable) throws IOException, T, InterruptedException; /** * Makes an asynchronous remote procedure call. * *

* Similar to {@link #call(Callable)} but returns immediately. * The result of the {@link Callable} can be obtained through the {@link Future} object. * * @return * The {@link Future} object that can be used to wait for the completion. * @throws IOException * If there's an error during the communication. */ Future callAsync(final Callable callable) throws IOException; /** * Performs an orderly shut down of this channel (and the remote peer.) * * @throws IOException * if the orderly shut-down failed. */ void close() throws IOException; /** * Waits for this {@link Channel} to be closed down. * * The close-down of a {@link Channel} might be initiated locally or remotely. * * @throws InterruptedException * If the current thread is interrupted while waiting for the completion. * @since 1.300 */ public void join() throws InterruptedException; /** * Waits for this {@link Channel} to be closed down, but only up the given milliseconds. * * @throws InterruptedException * If the current thread is interrupted while waiting for the completion. * @since 1.300 */ public void join(long timeout) throws InterruptedException; /** * Exports an object for remoting to the other {@link Channel} * by creating a remotable proxy. * *

* All the parameters and return values must be serializable. * * @param type * Interface to be remoted. * @return * the proxy object that implements T. This object can be transfered * to the other {@link Channel}, and calling methods on it from the remote side * will invoke the same method on the given local instance object. */ T export( Class type, T instance); /** * Blocks until all the I/O packets sent from remote is fully locally executed, then return. * * @since 1.402 */ void syncLocalIO() throws InterruptedException; } jenkins-remoting-2.23/src/main/java/hudson/remoting/Request.java0000644000175000017500000003556312122627370025466 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.Serializable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; /** * Request/response pattern over {@link Channel}, the layer-1 service. * *

* This assumes that the receiving side has all the class definitions * available to de-serialize {@link Request}, just like {@link Command}. * * @author Kohsuke Kawaguchi * @see Response */ abstract class Request extends Command { /** * Executed on a remote system to perform the task. * * @param channel * The local channel. From the view point of the JVM that * {@link #call(Channel) made the call}, this channel is * the remote channel. * @return * the return value will be sent back to the calling process. * @throws EXC * The exception will be forwarded to the calling process. * If no checked exception is supposed to be thrown, use {@link RuntimeException}. */ protected abstract RSP perform(Channel channel) throws EXC; /** * Uniquely identifies this request. * Used for correlation between request and response. */ private final int id; /** * Set by the sender to the ID of the last I/O issued from the sender thread. * The receiver will ensure that this I/O operation has completed before carrying out the task. * *

* If the sender doesn't support this, the receiver will see 0. */ private int lastIoId; private volatile Response response; /** * While executing the call this is set to the handle of the execution. */ protected volatile transient Future future; /** * Set by {@link Response} to point to the I/O ID issued from the other side that this request needs to * synchronize with, before declaring the call to be complete. */ /*package*/ volatile transient int responseIoId; /** * * @deprecated as of 2.16 * {@link PipeWriter} does this job better, but kept for backward compatibility to communicate * with earlier version of remoting without losing the original fix to JENKINS-9189 completely. */ /*package*/ volatile transient Future lastIo; protected Request() { synchronized(Request.class) { id = nextId++; } } /** * Sends this request to a remote system, and blocks until we receives a response. * * @param channel * The channel from which the request will be sent. * @throws InterruptedException * If the thread is interrupted while it's waiting for the call to complete. * @throws IOException * If there's an error during the communication. * @throws RequestAbortedException * If the channel is terminated while the call is in progress. * @throws EXC * If the {@link #perform(Channel)} throws an exception. */ public final RSP call(Channel channel) throws EXC, InterruptedException, IOException { lastIoId = channel.lastIoId(); // Channel.send() locks channel, and there are other call sequences // ( like Channel.terminate()->Request.abort()->Request.onCompleted() ) // that locks channel -> request, so lock objects in the same order synchronized(channel) { synchronized(this) { response=null; channel.pendingCalls.put(id,this); channel.send(this); } } try { synchronized(this) { // set the thread name to represent the channel we are blocked on, // so that thread dump would give us more useful information. Thread t = Thread.currentThread(); final String name = t.getName(); try { // wait until the response arrives t.setName(name+" / waiting for "+channel); while(response==null && !channel.isInClosed()) // I don't know exactly when this can happen, as pendingCalls are cleaned up by Channel, // but in production I've observed that in rare occasion it can block forever, even after a channel // is gone. So be defensive against that. wait(30*1000); if (response==null) // channel is closed and we still don't have a response throw new RequestAbortedException(null); } finally { t.setName(name); } if (lastIo != null) try { lastIo.get(); } catch (ExecutionException e) { // ignore the I/O error } try { channel.pipeWriter.get(responseIoId).get(); } catch (ExecutionException e) { // ignore the I/O error } Object exc = response.exception; if (exc!=null) { if(exc instanceof RequestAbortedException) { // add one more exception, so that stack trace shows both who's waiting for the completion // and where the connection outage was detected. exc = new RequestAbortedException((RequestAbortedException)exc); } throw (EXC)exc; // some versions of JDK fails to compile this line. If so, upgrade your JDK. } return response.returnValue; } } catch (InterruptedException e) { // if we are cancelled, abort the remote computation, too. // do this outside the "synchronized(this)" block to prevent locking Request and Channel in a wrong order. synchronized (channel) { // ... so that the close check and send won't be interrupted in the middle by a close if (!channel.isOutClosed()) channel.send(new Cancel(id)); // only send a cancel if we can, or else ChannelClosedException will mask the original cause } throw e; } } /** * Makes an invocation but immediately returns without waiting for the completion * (AKA asynchronous invocation.) * * @param channel * The channel from which the request will be sent. * @return * The {@link Future} object that can be used to wait for the completion. * @throws IOException * If there's an error during the communication. */ public final hudson.remoting.Future callAsync(final Channel channel) throws IOException { response=null; lastIoId = channel.lastIoId(); channel.pendingCalls.put(id,this); channel.send(this); return new hudson.remoting.Future() { private volatile boolean cancelled; public boolean cancel(boolean mayInterruptIfRunning) { if (cancelled || isDone()) { return false; } cancelled = true; if (mayInterruptIfRunning) { try { channel.send(new Cancel(id)); } catch (IOException x) { return false; } } return true; } public boolean isCancelled() { return cancelled; } public boolean isDone() { return isCancelled() || response!=null; } public RSP get() throws InterruptedException, ExecutionException { synchronized(Request.this) { try { while(response==null) { if (isCancelled()) { throw new CancellationException(); } Request.this.wait(); // wait until the response arrives } } catch (InterruptedException e) { try { channel.send(new Cancel(id)); } catch (IOException e1) { // couldn't cancel. ignore. } throw e; } if(response.exception!=null) throw new ExecutionException(response.exception); return response.returnValue; } } public RSP get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { synchronized(Request.this) { // wait until the response arrives // Note that the wait method can wake up for no reasons at all (AKA spurious wakeup), long end = System.currentTimeMillis() + unit.toMillis(timeout); long now; while(response==null && (now=System.currentTimeMillis()) response) { this.response = response; notify(); } /** * Aborts the processing. The calling thread will receive an exception. */ /*package*/ void abort(IOException e) { onCompleted(new Response(id,0,new RequestAbortedException(e))); } /** * Schedules the execution of this request. */ protected final void execute(final Channel channel) { channel.executingCalls.put(id,this); future = channel.executor.submit(new Runnable() { private int startIoId; private int calcLastIoId() { int endIoId = channel.lastIoId(); if (startIoId==endIoId) return 0; return endIoId; } public void run() { try { Command rsp; CURRENT.set(Request.this); startIoId = channel.lastIoId(); try { // make sure any I/O preceding this has completed channel.pipeWriter.get(lastIoId).get(); RSP r = Request.this.perform(channel); // normal completion rsp = new Response(id,calcLastIoId(),r); } catch (Throwable t) { // error return rsp = new Response(id,calcLastIoId(),t); } finally { CURRENT.set(null); } if(chainCause) rsp.createdAt.initCause(createdAt); synchronized (channel) {// expand the synchronization block of the send() method to a check if(!channel.isOutClosed()) channel.send(rsp); } } catch (IOException e) { // communication error. // this means the caller will block forever logger.log(Level.SEVERE, "Failed to send back a reply",e); } finally { channel.executingCalls.remove(id); } } }); } /** * Next request ID. */ private static int nextId=0; private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(Request.class.getName()); /** * Set to true to chain {@link Command#createdAt} to track request/response relationship. * This will substantially increase the network traffic, but useful for debugging. */ public static boolean chainCause = Boolean.getBoolean(Request.class.getName()+".chainCause"); /** * Set to the {@link Request} object during {@linkplain #perform(Channel) the execution of the call}. * * @deprecated as of 2.16 * {@link PipeWriter} does this job better, but kept for backward compatibility to communicate * with earlier version of remoting without losing the original fix to JENKINS-9189 completely. */ /*package*/ static ThreadLocal CURRENT = new ThreadLocal(); /*package*/ static int getCurrentRequestId() { Request r = CURRENT.get(); return r!=null ? r.id : 0; } /** * Interrupts the execution of the remote computation. */ private static final class Cancel extends Command { private final int id; Cancel(int id) { this.id = id; } protected void execute(Channel channel) { Request r = channel.executingCalls.get(id); if(r==null) return; // already completed Future f = r.future; if(f!=null) f.cancel(true); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/PreloadJarTask.java0000644000175000017500000000412312122627370026670 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.net.URL; /** * {@link Callable} used to deliver a jar file to {@link RemoteClassLoader}. * * @author Kohsuke Kawaguchi */ final class PreloadJarTask implements DelegatingCallable { /** * Jar file to be preloaded. */ private final URL[] jars; private transient ClassLoader target; PreloadJarTask(URL[] jars, ClassLoader target) { this.jars = jars; this.target = target; } public ClassLoader getClassLoader() { return target; } public Boolean call() throws IOException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (!(cl instanceof RemoteClassLoader)) return false; RemoteClassLoader rcl = (RemoteClassLoader) cl; boolean r = false; for (URL jar : jars) r |= rcl.prefetch(jar); return r; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/BinarySafeStream.java0000644000175000017500000003125212122627370027224 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.FilterOutputStream; import java.io.UnsupportedEncodingException; import java.io.ByteArrayOutputStream; import java.util.Arrays; /** * Tunnels byte stream into another byte stream so that binary data * can be sent across binary-unsafe stream. * *

* This implementation uses a variation of base64. A care has been * taken to ensure that the following scenario is handled correctly. * *

    *
  1. * If the writing side flush, the reading side should see everything * written by then, without blocking (even if this happens outside the 3-byte boundary) *
  2. * Reading side won't block unnecessarily. *
* * @author Kohsuke Kawaguchi */ public final class BinarySafeStream { // no instantiation private BinarySafeStream() {} /** * Decode binary safe stream. */ public static InputStream wrap(InputStream in) { return new FilterInputStream(in) { /** * Place a part of the decoded triplet that hasn's read by the caller yet. * We allocate four bytes because of the way we implement {@link #read(byte[], int, int)}, * which puts encoded base64 in the given array during the computation. */ final byte[] triplet = new byte[4]; /** * Remaining number of valid data in {@link #triplet}. * -1 to indicate EOF. Otherwise always 0-2. * Valid data starts at triplet[3-remaining] */ int remaining=0; final byte[] qualtet = new byte[4]; int input = 0; public int read() throws IOException { if(remaining==0) { remaining = _read(triplet,0,3); if(00; return ((int) triplet[3 - remaining--]) & 0xFF; } public int read(byte b[], int off, int len) throws IOException { if(remaining==-1) return -1; // EOF if(len<4) { // not enough space to process encoded data in the given buffer, so revert to the read-by-char int read = 0; int ch; while(len>0 && (ch=read())!=-1) { b[off++] = (byte)ch; read++; len--; } return read; } // first copy any remaining bytes in triplet to output int l = Math.min(len,remaining); if(l>0) { System.arraycopy(triplet,3-remaining,b,off,l); off+=l; len-=l; remaining=0; if(super.available()==0) // the next read() may block, so let's return now return l; if(len<4) // not enough space to call _read(). abort. return l; // otherwise try to read more int r = _read(b,off,len); if(r==-1) return l; else return l+r; } return _read(b,off,len); } /** * The same as {@link #read(byte[], int, int)} but the buffer must be * longer than off+4, */ private int _read(byte b[], int off, int len) throws IOException { assert remaining==0; assert b.length>=off+4; int totalRead = 0; // read in the rest if(len>0) { // put the remaining data from previous run at the top. if(input>0) System.arraycopy(qualtet,0, b, off,input); // for us to return any byte we need to at least read 4 bytes, // so insist on getting four bytes at least. When stream is flushed // we get extra '=' in the middle. int l=input; // l = # of total encoded bytes to be processed in this round while(l<4) { int r = super.read(b, off + l, Math.max(len,4) - l); if(r==-1) { if(l%4!=0) throw new IOException("Unexpected stream termination"); if(l==0) return -1; // EOF, and no data to process } l += r; } // we can only decode multiple of 4, so write back any remaining data to qualtet. // this also updates 'input' correctly. input = l%4; if(input>0) { System.arraycopy(b, off +l-input,qualtet,0,input); l-=input; } // now we just need to convert four at a time assert l%4==0; for( int base= off; l>0; l-=4 ) { // convert b[base...base+3] to b[off...off+2] // note that the buffer can be overlapping int c0 = DECODING_TABLE[b[base++]]; int c1 = DECODING_TABLE[b[base++]]; int c2 = DECODING_TABLE[b[base++]]; int c3 = DECODING_TABLE[b[base++]]; if(c0<0 || c1<0 || c2<-1 || c3<-1) { // illegal input. note that '=' never shows up as 1st or 2nd char // hence the check for the 1st half and 2nd half are different. // now try to report what we saw. // the remaining buffer is b[base-4 ... base-4+l] ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(b,base-4,l); // plus we might be able to read more bytes from the underlying stream int avail = super.available(); if(avail >0) { byte[] buf = new byte[avail]; baos.write(buf,0,super.read(buf)); } StringBuilder buf = new StringBuilder("Invalid encoded sequence encountered:"); for (byte ch : baos.toByteArray()) buf.append(String.format(" %02X",ch)); throw new IOException(buf.toString()); } b[off++] = (byte) ((c0<<2) | (c1>>4)); totalRead++; if(c2!=-1) { b[off++] = (byte) ((c1<<4) | (c2>>2)); totalRead++; if(c3!=-1) { b[off++] = (byte) ((c2<<6) | c3); totalRead++; } } } } return totalRead; } public int available() throws IOException { // roughly speaking we got 3/4 of the underlying available bytes return super.available()*3/4; } }; } /** * Wraps an {@link OutputStream} to encoding {@link OutputStream}. * * @param out * This output stream should be buffered for better performance. */ public static OutputStream wrap(OutputStream out) { return new FilterOutputStream(out) { private final byte[] triplet = new byte[3]; private int remaining=0; private final byte[] out = new byte[4]; public void write(int b) throws IOException { if(remaining==2) { _write(triplet[0],triplet[1],(byte)b); remaining=0; } else { triplet[remaining++]=(byte)b; } } public void write(byte b[], int off, int len) throws IOException { // if there's anything left in triplet from the last write, try to write them first if(remaining>0) { while(len>0 && remaining<3) { triplet[remaining++] = b[off++]; len--; } if(remaining==3) { _write(triplet[0],triplet[1],triplet[2]); remaining = 0; } } // then convert chunks as much as possible while(len>=3) { _write(b[off++],b[off++],b[off++]); len-=3; } // store remaining stuff back to triplet assert 0<=len && len<3; while(len>0) { triplet[remaining++] = b[off++]; len--; } } private void _write(byte a, byte b, byte c) throws IOException { out[0] = ENCODING_TABLE[(a>>2)&0x3F]; out[1] = ENCODING_TABLE[((a<<4)&0x3F|(b>>4)&0x0F)]; out[2] = ENCODING_TABLE[((b<<2)&0x3F|(c>>6)&0x03)]; out[3] = ENCODING_TABLE[c&0x3F]; super.out.write(out,0,4); } public void flush() throws IOException { int a = triplet[0]; int b = triplet[1]; a&=0xFF; b&=0xFF; switch(remaining) { case 0: // noop break; case 1: out[0] = ENCODING_TABLE[(a>>2)&0x3F]; out[1] = ENCODING_TABLE[(a<<4)&0x3F]; out[2] = '='; out[3] = '='; super.out.write(out,0,4); remaining = 0; break; case 2: out[0] = ENCODING_TABLE[(a>>2)&0x3F]; out[1] = ENCODING_TABLE[((a<<4)|(b>>4))&0x3F]; out[2] = ENCODING_TABLE[(b<<2)&0x3F]; out[3] = '='; super.out.write(out,0,4); remaining = 0; break; default: throw new AssertionError(); } super.flush(); } }; } private static final byte[] ENCODING_TABLE; /** * 0-63 are the index value, -1 is '='. * -2 indicates all the other illegal characters. */ private static final int[] DECODING_TABLE = new int[128]; static { try { ENCODING_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes("US-ASCII"); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } Arrays.fill(DECODING_TABLE,-2); for (int i = 0; i < ENCODING_TABLE.length; i++) DECODING_TABLE[ENCODING_TABLE[i]] = i; DECODING_TABLE['='] = -1; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/FastPipedInputStream.java0000644000175000017500000001517212122627370030103 0ustar jamespagejamespage/* * @(#)$Id: FastPipedInputStream.java 3619 2008-03-26 07:23:03Z yui $ * * Copyright 2006-2008 Makoto YUI * * Licensed 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. * * Contributors: * Makoto YUI - initial implementation */ package hudson.remoting; import java.io.InputStream; import java.io.IOException; import java.lang.ref.WeakReference; /** * This class is equivalent to java.io.PipedInputStream. In the * interface it only adds a constructor which allows for specifying the buffer * size. Its implementation, however, is much simpler and a lot more efficient * than its equivalent. It doesn't rely on polling. Instead it uses proper * synchronization with its counterpart {@link FastPipedOutputStream}. * * @author WD * @link http://developer.java.sun.com/developer/bugParade/bugs/4404700.html * @see FastPipedOutputStream */ public class FastPipedInputStream extends InputStream { final byte[] buffer; /** * Once closed, this is set to the stack trace of who closed it. */ ClosedBy closed = null; int readLaps = 0; int readPosition = 0; WeakReference source; int writeLaps = 0; int writePosition = 0; private final Throwable allocatedAt = new Throwable(); /** * Creates an unconnected PipedInputStream with a default buffer size. */ public FastPipedInputStream() { this.buffer = new byte[0x10000]; } /** * Creates a PipedInputStream with a default buffer size and connects it to * source. * @exception IOException It was already connected. */ public FastPipedInputStream(FastPipedOutputStream source) throws IOException { this(source, 0x10000 /* 65536 */); } /** * Creates a PipedInputStream with buffer size bufferSize and * connects it to source. * @exception IOException It was already connected. */ public FastPipedInputStream(FastPipedOutputStream source, int bufferSize) throws IOException { if(source != null) { connect(source); } this.buffer = new byte[bufferSize]; } private FastPipedOutputStream source() throws IOException { FastPipedOutputStream s = source.get(); if (s==null) throw (IOException)new IOException("Writer side has already been abandoned").initCause(allocatedAt); return s; } @Override public int available() throws IOException { /* The circular buffer is inspected to see where the reader and the writer * are located. */ synchronized (buffer) { return writePosition > readPosition /* The writer is in the same lap. */? writePosition - readPosition : (writePosition < readPosition /* The writer is in the next lap. */? buffer.length - readPosition + 1 + writePosition : /* The writer is at the same position or a complete lap ahead. */ (writeLaps > readLaps ? buffer.length : 0)); } } /** * @exception IOException The pipe is not connected. */ @Override public void close() throws IOException { if(source == null) { throw new IOException("Unconnected pipe"); } synchronized(buffer) { closed = new ClosedBy(); // Release any pending writers. buffer.notifyAll(); } } /** * @exception IOException The pipe is already connected. */ public void connect(FastPipedOutputStream source) throws IOException { if(this.source != null) { throw new IOException("Pipe already connected"); } this.source = new WeakReference(source); source.sink = new WeakReference(this); } @Override protected void finalize() throws Throwable { super.finalize(); close(); } @Override public void mark(int readLimit) { } @Override public boolean markSupported() { return false; } public int read() throws IOException { byte[] b = new byte[1]; return read(b, 0, b.length) == -1 ? -1 : (255 & b[0]); } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /** * @exception IOException The pipe is not connected. */ @Override public int read(byte[] b, int off, int len) throws IOException { if(source == null) { throw new IOException("Unconnected pipe"); } while (true) { synchronized(buffer) { if(writePosition == readPosition && writeLaps == readLaps) { if(closed!=null) { return -1; } source(); // make sure the sink is still trying to read, or else fail the write. // Wait for any writer to put something in the circular buffer. try { buffer.wait(FastPipedOutputStream.TIMEOUT); } catch (InterruptedException e) { throw new IOException(e.getMessage()); } // Try again. continue; } // Don't read more than the capacity indicated by len or what's available // in the circular buffer. int amount = Math.min(len, (writePosition > readPosition ? writePosition : buffer.length) - readPosition); System.arraycopy(buffer, readPosition, b, off, amount); readPosition += amount; if(readPosition == buffer.length) {// A lap was completed, so go back. readPosition = 0; ++readLaps; } buffer.notifyAll(); return amount; } } } static final class ClosedBy extends Throwable { ClosedBy() { super("The pipe was closed at..."); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/DelegatingCallable.java0000644000175000017500000000377412122627370027520 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * {@link Callable} that nominates another claassloader for serialization. * *

* For various reasons, one {@link Callable} object (and all the objects reachable from it) is * serialized by one classloader. * By default, the classloader that loaded {@link Callable} object itself is used, * but when {@link Callable} object refers to other objects that are loaded by other classloaders, * this will fail to deserialize on the remote end. * *

* In such a case, implement this interface, instead of plain {@link Callable} and * return a classloader that can see all the classes. * * In case of Hudson, {@code PluginManager.uberClassLoader} is a good candidate. * * @author Kohsuke Kawaguchi */ public interface DelegatingCallable extends Callable { ClassLoader getClassLoader(); } jenkins-remoting-2.23/src/main/java/hudson/remoting/UnexportCommand.java0000644000175000017500000000304112122627370027143 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * {@link Command} that unexports an object. * @author Kohsuke Kawaguchi */ public class UnexportCommand extends Command { private final int oid; public UnexportCommand(int oid) { this.oid = oid; } protected void execute(Channel channel) { channel.unexport(oid); } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/StandardOutputStream.java0000644000175000017500000000650412122627370030164 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2011, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; /** * Hint that indicates that we are using {@code stdout} with fd=1 as the stream to write to for the channel. * *

* Using stdin/stdout pair for the communication is very convenient for setting up a remote channel, * so Jenkins tends to do that, but even with our using {@link System#setOut(PrintStream)} to avoid * other parts of the Java code to accidentally corrupt the stream, file descriptor 1 continues to * point to the stream we use, so native code in JVM can still accidentally pollute the stream. * *

* Fixing that requires the use of dup and close POSIX API calls, which we can only do after the communication * gets established and JNA is brought in via remoting. Having {@link Launcher} uses this wrapper class allows * us to do that without recreating the stream. * * @author Kohsuke Kawaguchi */ public class StandardOutputStream extends OutputStream { private OutputStream out; private boolean swapped; public StandardOutputStream() { this.out = System.out; } /** * Atomically swaps the underlying stream to another stream and returns it. */ public synchronized OutputStream swap(OutputStream another) { // if the stream is already swapped, the caller's implicit assumption that this represents stdout // is wrong, so we raise an error. if (swapped) throw new IllegalStateException(); OutputStream old = out; out = another; swapped = true; return old; } public synchronized boolean isSwapped() { return swapped; } @Override public synchronized void write(int b) throws IOException { out.write(b); } @Override public synchronized void write(byte[] b) throws IOException { out.write(b); } @Override public synchronized void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); } @Override public synchronized void flush() throws IOException { out.flush(); } @Override public synchronized void close() throws IOException { out.close(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/RemoteInvocationHandler.java0000644000175000017500000002615412122627370030615 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Sits behind a proxy object and implements the proxy logic. * * @author Kohsuke Kawaguchi */ final class RemoteInvocationHandler implements InvocationHandler, Serializable { /** * This proxy acts as a proxy to the object of * Object ID on the remote {@link Channel}. */ private final int oid; /** * Represents the connection to the remote {@link Channel}. * *

* This field is null when a {@link RemoteInvocationHandler} is just * created and not working as a remote proxy. Once tranferred to the * remote system, this field is set to non-null. */ private transient Channel channel; /** * True if we are proxying an user object. */ private final boolean userProxy; /** * If true, this proxy is automatically unexported by the calling {@link Channel}, * so this object won't release the object at {@link #finalize()}. *

* This ugly distinction enables us to keep the # of exported objects low for * the typical situation where the calls are synchronous (thus end of the call * signifies full unexport of all involved objects.) */ private final boolean autoUnexportByCaller; /** * If true, indicates that this proxy object is being sent back * to where it came from. If false, indicates that this proxy * is being sent to the remote peer. * * Only used in the serialized form of this class. */ private boolean goingHome; /** * Creates a proxy that wraps an existing OID on the remote. */ RemoteInvocationHandler(Channel channel, int id, boolean userProxy, boolean autoUnexportByCaller) { this.channel = channel; this.oid = id; this.userProxy = userProxy; this.autoUnexportByCaller = autoUnexportByCaller; } /** * Wraps an OID to the typed wrapper. */ public static T wrap(Channel channel, int id, Class type, boolean userProxy, boolean autoUnexportByCaller) { ClassLoader cl = type.getClassLoader(); // if the type is a JDK-defined type, classloader should be for IReadResolve if(cl==null || cl==ClassLoader.getSystemClassLoader()) cl = IReadResolve.class.getClassLoader(); return type.cast(Proxy.newProxyInstance(cl, new Class[]{type,IReadResolve.class}, new RemoteInvocationHandler(channel,id,userProxy,autoUnexportByCaller))); } /** * If the given object is a proxy to a remote object in the specified channel, * return its object ID. Otherwise return -1. *

* This method can be used to get back the original reference when * a proxy is sent back to the channel it came from. */ public static int unwrap(Object proxy, Channel src) { InvocationHandler h = Proxy.getInvocationHandler(proxy); if (h instanceof RemoteInvocationHandler) { RemoteInvocationHandler rih = (RemoteInvocationHandler) h; if(rih.channel==src) return rih.oid; } return -1; } /** * If the given object is a proxy object, return the {@link Channel} * object that it's associated with. Otherwise null. */ public static Channel unwrap(Object proxy) { InvocationHandler h = Proxy.getInvocationHandler(proxy); if (h instanceof RemoteInvocationHandler) { RemoteInvocationHandler rih = (RemoteInvocationHandler) h; return rih.channel; } return null; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getDeclaringClass()==IReadResolve.class) { // readResolve on the proxy. // if we are going back to where we came from, replace the proxy by the real object if(goingHome) return channel.getExportedObject(oid); else return proxy; } if(channel==null) throw new IllegalStateException("proxy is not connected to a channel"); if(args==null) args = EMPTY_ARRAY; Class dc = method.getDeclaringClass(); if(dc ==Object.class) { // handle equals and hashCode by ourselves try { return method.invoke(this,args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } // delegate the rest of the methods to the remote object if(userProxy) return channel.call(new RPCRequest(oid,method,args,dc.getClassLoader())); else return new RPCRequest(oid,method,args).call(channel); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { channel = Channel.current(); ois.defaultReadObject(); } private void writeObject(ObjectOutputStream oos) throws IOException { goingHome = channel!=null; oos.defaultWriteObject(); } /** * Two proxies are the same iff they represent the same remote object. */ public boolean equals(Object o) { if(Proxy.isProxyClass(o.getClass())) o = Proxy.getInvocationHandler(o); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RemoteInvocationHandler that = (RemoteInvocationHandler) o; return this.oid==that.oid && this.channel==that.channel; } public int hashCode() { return oid; } protected void finalize() throws Throwable { // unexport the remote object if(channel!=null && !autoUnexportByCaller) channel.send(new UnexportCommand(oid)); super.finalize(); } private static final long serialVersionUID = 1L; /** * Executes the method call remotely. * * If used as {@link Request}, this can be used to provide a lower-layer * for the use inside remoting, to implement the classloader delegation, and etc. * The downside of this is that the classes used as a parameter/return value * must be available to both JVMs. * * If used as {@link Callable} in conjunction with {@link UserRequest}, * this can be used to send a method call to user-level objects, and * classes for the parameters and the return value are sent remotely if needed. */ static final class RPCRequest extends Request implements DelegatingCallable { /** * Target object id to invoke. */ private final int oid; private final String methodName; /** * Type name of the arguments to invoke. They are names because * neither {@link Method} nor {@link Class} is serializable. */ private final String[] types; /** * Arguments to invoke the method with. */ private final Object[] arguments; /** * If this is used as {@link Callable}, we need to remember what classloader * to be used to serialize the request and the response. */ private transient ClassLoader classLoader; public RPCRequest(int oid, Method m, Object[] arguments) { this(oid,m,arguments,null); } public RPCRequest(int oid, Method m, Object[] arguments, ClassLoader cl) { this.oid = oid; this.arguments = arguments; this.methodName = m.getName(); this.classLoader = cl; this.types = new String[arguments.length]; Class[] params = m.getParameterTypes(); for( int i=0; i[] paramTypes = m.getParameterTypes(); if(paramTypes.length!=arguments.length) continue; for( int i=0; i * Note that this class by itself does not perform buffering. * * @author Kohsuke Kawaguchi */ public class RemoteInputStream extends InputStream implements Serializable { private transient InputStream core; private boolean autoUnexport; /** * Short for {@code RemoteInputStream(core,true)}. */ public RemoteInputStream(InputStream core) { this(core,true); } /** * @param autoUnexport * If true, the {@link InputStream} will be automatically unexported when * the callable that took it with returns. If false, it'll not unexported * until the close method is called. */ public RemoteInputStream(InputStream core, boolean autoUnexport) { this.core = core; this.autoUnexport = autoUnexport; } private void writeObject(ObjectOutputStream oos) throws IOException { int id = Channel.current().export(core,autoUnexport); oos.writeInt(id); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { final Channel channel = Channel.current(); assert channel !=null; this.core = new ProxyInputStream(channel, ois.readInt()); } private static final long serialVersionUID = 1L; // // // delegation to core // // public int read() throws IOException { return core.read(); } public int read(byte[] b) throws IOException { return core.read(b); } public int read(byte[] b, int off, int len) throws IOException { return core.read(b, off, len); } public long skip(long n) throws IOException { return core.skip(n); } public int available() throws IOException { return core.available(); } public void close() throws IOException { core.close(); } public void mark(int readlimit) { core.mark(readlimit); } public void reset() throws IOException { core.reset(); } public boolean markSupported() { return core.markSupported(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/0000755000175000017500000000000012122627370024122 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/GuiListener.java0000644000175000017500000000571512122627370027227 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.jnlp; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import hudson.remoting.EngineListener; import java.io.StringWriter; import java.io.PrintWriter; import java.util.logging.Logger; import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; /** * {@link EngineListener} implementation that shows GUI. */ public final class GuiListener implements EngineListener { public final MainDialog frame; public GuiListener() { GUI.setUILookAndFeel(); frame = new MainDialog(); frame.setVisible(true); } public void status(final String msg) { status(msg,null); } public void status(final String msg, final Throwable t) { SwingUtilities.invokeLater(new Runnable() { public void run() { frame.status(msg); if(t!=null) LOGGER.log(INFO, msg, t); } }); } public void error(final Throwable t) { SwingUtilities.invokeLater(new Runnable() { public void run() { LOGGER.log(SEVERE, t.getMessage(), t); StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); JOptionPane.showMessageDialog( frame,sw.toString(),"Error", JOptionPane.ERROR_MESSAGE); System.exit(-1); } }); } public void onDisconnect() { SwingUtilities.invokeLater(new Runnable() { public void run() { // discard all the menu items that might have been added by the master. frame.resetMenuBar(); } }); } private static final Logger LOGGER = Logger.getLogger(GuiListener.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/MainDialog.java0000644000175000017500000000647212122627370027002 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.jnlp; import hudson.remoting.Engine; import javax.swing.*; import java.awt.*; /** * Main window for JNLP slave agent. * * @author Kohsuke Kawaguchi */ public class MainDialog extends JFrame { private MainMenu mainMenu; private final JLabel statusLabel; public MainDialog() throws HeadlessException { super("Jenkins slave agent"); ImageIcon background = new ImageIcon(getClass().getResource("title.png")); JPanel foregroundPanel = new JPanel(new BorderLayout(10, 10)); foregroundPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); foregroundPanel.setOpaque(false); statusLabel = new JLabel("",JLabel.TRAILING); foregroundPanel.add(statusLabel, BorderLayout.CENTER); setContentPane(GUI.wrapInBackgroundImage(foregroundPanel, background,JLabel.BOTTOM,JLabel.LEADING)); resetMenuBar(); pack(); setSize(new Dimension(250,150)); getContentPane().setBackground(Color.WHITE); setLocationByPlatform(true); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); } /** * Gets the main menu of this window, so that the caller can add * additional menu items. * * @return never null. */ public MainMenu getMainMenu() { return mainMenu; } public void resetMenuBar() { mainMenu = new MainMenu(this); if(mainMenu.getComponentCount()>0) { setJMenuBar(mainMenu); mainMenu.commit(); } else { setJMenuBar(null); if(isVisible()) setVisible(true); // work around for paint problem. see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4949810 } } public void status(String msg) { statusLabel.setText(msg); } /** * If the current JVM runs a {@link MainDialog} as a JNLP slave agent, * return its reference, otherwise null. */ public static MainDialog get() { Engine e = Engine.current(); if(e==null) return null; if (!(e.listener instanceof GuiListener)) return null; return ((GuiListener) e.listener).frame; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/GUI.java0000644000175000017500000000720312122627370025413 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.jnlp; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.JPanel; import javax.swing.JComponent; import javax.swing.Icon; import javax.swing.JLabel; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Dimension; /** * GUI related helper code * @author Kohsuke Kawaguchi */ public class GUI { /** * Sets to the platform native look and feel. * * see http://javaalmanac.com/egs/javax.swing/LookFeelNative.html */ public static void setUILookAndFeel() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (InstantiationException e) { } catch (ClassNotFoundException e) { } catch (UnsupportedLookAndFeelException e) { } catch (IllegalAccessException e) { } } // Set up contraints so that the user supplied component and the // background image label overlap and resize identically private static final GridBagConstraints gbc; static { gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; gbc.anchor = GridBagConstraints.NORTHWEST; } public static JPanel wrapInBackgroundImage(JComponent component, Icon backgroundIcon, int verticalAlignment, int horizontalAlignment) { // make the passed in swing component transparent component.setOpaque(false); // create wrapper JPanel JPanel backgroundPanel = new JPanel(new GridBagLayout()); // add the passed in swing component first to ensure that it is in front backgroundPanel.add(component, gbc); // create a label to paint the background image JLabel backgroundImage = new JLabel(backgroundIcon); // set minimum and preferred sizes so that the size of the image // does not affect the layout size backgroundImage.setPreferredSize(new Dimension(1,1)); backgroundImage.setMinimumSize(new Dimension(1,1)); // align the image as specified. backgroundImage.setVerticalAlignment(verticalAlignment); backgroundImage.setHorizontalAlignment(horizontalAlignment); // add the background label backgroundPanel.add(backgroundImage, gbc); // return the wrapper return backgroundPanel; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/MainMenu.java0000644000175000017500000000436512122627370026506 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.jnlp; import javax.swing.*; import java.awt.event.KeyEvent; /** * Main menu of {@link MainDialog}. * * @author Kohsuke Kawaguchi */ public final class MainMenu extends JMenuBar { private final MainDialog owner; private JMenu fileMenu; MainMenu(MainDialog owner) { this.owner = owner; } /** * Obtains the file menu (and creates it if necessary), * so that the caller can add items in this menu. */ public JMenu getFileMenu() { if(fileMenu==null) { fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F); add(fileMenu,0); } return fileMenu; } /** * Reflects the changes made in the menu objects to GUI. */ public void commit() { invalidate(); repaint(); if(getComponentCount()>0) { owner.setJMenuBar(this); // work around for paint problem. see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4949810 if(owner.isVisible()) owner.setVisible(true); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/package-info.java0000644000175000017500000000262712122627370027320 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * Code for launching the remote agent through JNLP. * *

* This involves in getting the connection parameters through command line, * which is provided from slave-agent.jnlp.jelly file in the core. */ package hudson.remoting.jnlp; jenkins-remoting-2.23/src/main/java/hudson/remoting/jnlp/Main.java0000644000175000017500000001411712122627370025655 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.jnlp; import org.kohsuke.args4j.Option; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import java.util.logging.Logger; import java.util.logging.Level; import static java.util.logging.Level.INFO; import java.util.List; import java.util.ArrayList; import java.net.URL; import java.io.IOException; import hudson.remoting.Engine; import hudson.remoting.EngineListener; /** * Entry point to JNLP slave agent. * *

* See also slave-agent.jnlp.jelly in the core. * * @author Kohsuke Kawaguchi */ public class Main { @Option(name="-tunnel",metaVar="HOST:PORT", usage="Connect to the specified host and port, instead of connecting directly to Hudson. " + "Useful when connection to Hudson needs to be tunneled. Can be also HOST: or :PORT, " + "in which case the missing portion will be auto-configured like the default behavior") public String tunnel; @Option(name="-headless", usage="Run in headless mode, without GUI") public boolean headlessMode = Boolean.getBoolean("hudson.agent.headless") || Boolean.getBoolean("hudson.webstart.headless"); @Option(name="-url", usage="Specify the Hudson root URLs to connect to.") public final List urls = new ArrayList(); @Option(name="-credentials",metaVar="USER:PASSWORD", usage="HTTP BASIC AUTH header to pass in for making HTTP requests.") public String credentials; @Option(name="-noreconnect", usage="If the connection ends, don't retry and just exit.") public boolean noReconnect = false; /** * 4 mandatory parameters. * Host name (deprecated), Hudson URL, secret key, and slave name. */ @Argument public final List args = new ArrayList(); public static void main(String[] args) throws IOException, InterruptedException { try { _main(args); } catch (CmdLineException e) { System.err.println(e.getMessage()); System.err.println("java -jar slave.jar [options...] "); new CmdLineParser(new Main()).printUsage(System.err); } } /** * Main without the argument handling. */ public static void _main(String[] args) throws IOException, InterruptedException, CmdLineException { // see http://forum.java.sun.com/thread.jspa?threadID=706976&tstart=0 // not sure if this is the cause, but attempting to fix // https://hudson.dev.java.net/issues/show_bug.cgi?id=310 // by overwriting the security manager. try { System.setSecurityManager(null); } catch (SecurityException e) { // ignore and move on. // some user reported that this happens on their JVM: http://d.hatena.ne.jp/tueda_wolf/20080723 } // if we run in Mac, put the menu bar where the user expects it System.setProperty("apple.laf.useScreenMenuBar", "true"); Main m = new Main(); CmdLineParser p = new CmdLineParser(m); p.parseArgument(args); if(m.args.size()!=2) throw new CmdLineException("two arguments required, but got "+m.args); if(m.urls.isEmpty()) throw new CmdLineException("At least one -url option is required."); m.main(); } public void main() throws IOException, InterruptedException { Engine engine = createEngine(); engine.start(); try { engine.join(); } finally { // if we are programmatically driven by other code, // allow them to interrupt our blocking main thread // to kill the on-going connection to Jenkins engine.interrupt(); } } public Engine createEngine() { Engine engine = new Engine( headlessMode ? new CuiListener() : new GuiListener(), urls, args.get(0), args.get(1)); if(tunnel!=null) engine.setTunnel(tunnel); if(credentials!=null) engine.setCredentials(credentials); engine.setNoReconnect(noReconnect); return engine; } /** * {@link EngineListener} implementation that sends output to {@link Logger}. */ private static final class CuiListener implements EngineListener { private CuiListener() { LOGGER.info("Jenkins agent is running in headless mode."); } public void status(String msg, Throwable t) { LOGGER.log(INFO,msg,t); } public void status(String msg) { status(msg,null); } public void error(Throwable t) { LOGGER.log(Level.SEVERE, t.getMessage(), t); System.exit(-1); } public void onDisconnect() { } } private static final Logger LOGGER = Logger.getLogger(Main.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/CommandTransport.java0000644000175000017500000001741312122627370027323 0ustar jamespagejamespagepackage hudson.remoting; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; /** * Lower level abstraction under {@link Channel} for sending and receiving commands * *

* {@link Channel} is internally implemented on top of a transport that satisfies * the following characteristics: * *

*
Point-to-point
*
* Like TCP, by the time {@link CommandTransport} is used by {@link Channel}, it needs to be * connected to "the other side". The write operation doesn't take the receiver address, * and the read operation doesn't return the sender address. A {@link CommandTransport} talks * to one and the only peer throughout its life. *
*
Packet-oriented
*
* Like UDP, each read/write operation acts on a single {@link Command} object, sent across * in its serialized form. *
*
Reliable and in-order
*
* {@link Command}s that are written need to arrive in the exact same order, without any loss, * or else both sides must raise an error, like TCP. *
*
* *

* {@linkplain ClassicCommandTransport the default traditional implementation} implements * this on top of a TCP-like bi-directional byte stream, but {@link Channel} can work with * any {@link CommandTransport} that provides a similar hook. * *

Serialization of {@link Command}

*

* {@link Command} objects need to be serialized and deseralized in a specific environment * so that {@link Command}s can access {@link Channel} that's using it. Because of this, * a transport needs to use {@link Command#writeTo(Channel, ObjectOutputStream)} and * {@link Command#readFrom(Channel, ObjectInputStream)}. * * @author Kohsuke Kawaguchi * @since 2.13 */ public abstract class CommandTransport { /** * Package private so as not to allow direct subtyping (just yet.) */ /*package*/ CommandTransport() { } /** * SPI implemented by {@link Channel} so that the transport can pass the received command * to {@link Channel} for processing. */ static interface CommandReceiver { /** * Notifies the channel that a new {@link Command} was received from the other side. * *

* {@link Channel} performs all the error recovery of the error resulting from the command invocation. * {@link Channel} implements this method in a non-reentrant fashion; a transport can invoke this method * from different threads, but as the class javadoc states, the transport must * guarantee in-order delivery of the commands, and that means you cannot call this method * concurrently. * * @param cmd * The command received. This object must be read from {@link ObjectInputStream} via * {@link Command#readFrom(Channel, ObjectInputStream)} */ void handle(Command cmd); /** * Indicates that the transport has encountered a problem. * This tells the channel that it shouldn't expect future invocation of {@link #handle(Command)}, * and it'll abort the communication. */ void terminate(IOException e); } /** * Abstraction of the connection hand-shaking. * *

* Before two channels can talk to each other, */ public abstract Capability getRemoteCapability() throws IOException; /** * Starts the transport. * * This method is called once and only once at the end of the initialization of {@link Channel}, * after the {@link #getRemoteCapability()} is invoked. * * The first purpose of this method is to provide a reference back to {@link Channel}, and * the second purpose of this method is to allow {@link CommandTransport} to message pumping, * where it starts receiving commands from the other side and pass them onto {@link CommandReceiver}. * * This abstraction enables asynchronous processing — for example you can have a single thread * serving a large number of {@link Channel}s via NIO. * * For subtypes that prefer synchronous operation, extend from {@link SynchronousCommandTransport}. * *

Closing the read pump

*

* {@link Channel} implements {@linkplain Channel.CloseCommand its own "end of command stream" marker}, and * therefore under the orderly shutdown scenario, it doesn't rely on the transport to provide EOF-like * marker. Instead, {@link Channel} will call your {@link #closeRead()} (from the same thread * that invoked {@link CommandReceiver#handle(Command)}) to indicate that it is done with the reading. * *

* If the transport encounters any error from the lower layer (say, the underlying TCP/IP socket * encountered a REST), then call {@link CommandReceiver#terminate(IOException)} to initiate the abnormal * channel termination. This in turn calls {@link #closeRead()} to shutdown the reader side. */ public abstract void setup(Channel channel, CommandReceiver receiver); /** * Called by {@link Channel} to send one command to the other side. * * {@link Channel} serializes the invocation of this method for ordering. That is, * at any given point in time only one thread executes this method. * *

* Asynchronous transport must serialize the given command object before * returning from this method, as its content can be modified by the calling * thread as soon as this method returns. Also, if an asynchronous transport * chooses to return from this method without committing data to the network, * then it is also responsible for a flow control (by blocking this method * if too many commands are queueing up waiting for the network to unclog.) * * @param cmd * The command object that needs to be sent. Never null. This must be * serialized via {@link Command#writeTo(Channel, ObjectOutputStream)} * @param last * Informational flag that indicates that this is the last * call of the {@link #write(Command, boolean)}. */ abstract void write(Command cmd, boolean last) throws IOException; /** * Called to close the write side of the transport, allowing the underlying transport * to be shut down. * *

* If the {@link Channel} aborts the communication, this method may end up invoked * asynchronously, concurrently, and multiple times. The implementation must protect * against that. */ public abstract void closeWrite() throws IOException; /** * Called to indicate that the no further input is expected and any resources * associated with reading commands should be freed. * * {@link Channel#isInClosed()} can be also used to test if the command reading * should terminate. */ public abstract void closeRead() throws IOException; /** * Historical artifact left for backward compatibility, necessary only for retaining * {@link Channel#getUnderlyingOutput()}. * *

* Historically, {@link CommandTransport} abstraction is introduced much later than * {@link Channel}. The implementation of the historical {@link Channel} was mostly * OK that lets us cleanly introduce the {@link CommandTransport} abstraction, but * there was one method in Channel that assumed that there's the underlying {@link OutputStream} * (that now belongs to the implementation details of {@link CommandTransport}), * hence this method. * *

* This method is package private, to prevent new {@link CommandTransport} from * providing this feature. */ OutputStream getUnderlyingStream() { return null; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/EngineListener.java0000644000175000017500000000345312122627370026742 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * Receives status notification from {@link Engine}. * * The callback will be invoked on a non-GUI thread. * * @author Kohsuke Kawaguchi */ public interface EngineListener { /** * Status message that indicates the progress of the operation. */ void status(String msg); /** * Status message, with additoinal stack trace that indicates an error that was recovered. */ void status(String msg, Throwable t); /** * Fatal error that's non recoverable. */ void error(Throwable t); /** * Called when a connection is terminated. */ void onDisconnect(); } jenkins-remoting-2.23/src/main/java/hudson/remoting/PipeWindow.java0000644000175000017500000001671312122627370026117 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2010, InfraDNA, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.OutputStream; import java.util.logging.Logger; import static java.util.logging.Level.*; /** * Keeps track of the number of bytes that the sender can send without overwhelming the receiver of the pipe. * *

* {@link OutputStream} is a blocking operation in Java, so when we send byte[] to the remote to write to * {@link OutputStream}, it needs to be done in a separate thread (or else we'll fail to attend to the channel * in timely fashion.) This in turn means the byte[] being sent needs to go to a queue between a * channel reader thread and I/O processing thread, and thus in turn means we need some kind of throttling * mechanism, or else the queue can grow too much. * *

* This implementation solves the problem by using TCP/IP like window size tracking. The sender allocates * a fixed length window size. Every time the sender sends something we reduce this value. When the receiver * writes data to {@link OutputStream}, it'll send back the "ack" command, which adds to this value, allowing * the sender to send more data. * * @author Kohsuke Kawaguchi */ abstract class PipeWindow { protected volatile Throwable dead; /** * Returns the current maximum window size. * *

* This is the size of the available window size if all the currrently in-flight bytes get acked. */ abstract int max(); /** * When we receive Ack from the receiver, we increase the window size by calling this method. */ abstract void increase(int delta); /** * Returns the current available window size. * * Unlike {@link #get(int)}, this method will never wait for the space to become available. */ abstract int peek(); /** * Returns the current available window size. * * If the available window size is smaller than the specified minimum size, * this method blocks until some space becomes available. * * @throws IOException * If we learned that there is an irrecoverable problem on the remote side that prevents us from writing. * @throws InterruptedException * If a thread was interrupted while blocking. * @return * The available window size >= min. * @param min */ abstract int get(int min) throws InterruptedException, IOException; /** * When we send out some bytes to the network, we decrease the window size by calling this method. */ abstract void decrease(int delta); /** * Indicates that the remote end has died and all the further send attempt should fail. */ void dead(Throwable cause) { this.dead = cause; } /** * If we already know that the remote end had developed a problem, throw an exception. * Otherwise no-op. */ protected void checkDeath() throws IOException { if (dead!=null) // the remote end failed to write. throw (IOException)new IOException("Pipe is already closed").initCause(dead); } /** * Fake implementation used when the receiver side doesn't support throttling. */ static class Fake extends PipeWindow { @Override int max() { return Integer.MAX_VALUE; } void increase(int delta) { } int peek() { return Integer.MAX_VALUE; } int get(int min) throws InterruptedException, IOException { checkDeath(); return Integer.MAX_VALUE; } void decrease(int delta) { } } static final class Key { public final int oid; Key(int oid) { this.oid = oid; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; return oid == ((Key) o).oid; } @Override public int hashCode() { return oid; } } static class Real extends PipeWindow { private final int initial; private int available; /** * Total bytes that left our side of the channel. */ private long written; /** * Total bytes that the remote side acked. */ private long acked; private final int oid; /** * The only strong reference to the key, which in turn * keeps this object accessible in {@link Channel#pipeWindows}. */ private final Key key; Real(Key key, int initialSize) { this.key = key; this.oid = key.oid; this.available = initialSize; this.initial = initialSize; } @Override int max() { return initial; } public synchronized void increase(int delta) { if (LOGGER.isLoggable(FINER)) LOGGER.finer(String.format("increase(%d,%d)->%d",oid,delta,delta+available)); available += delta; acked += delta; notifyAll(); } public synchronized int peek() { return available; } /** * Blocks until some space becomes available. */ public int get(int min) throws InterruptedException, IOException { checkDeath(); synchronized (this) { if (available>=min) return available; while (available%d",oid,delta,available-delta)); available -= delta; written+= delta; /* HUDSON-7745 says the following assertion fails, which AFAICT is only possible if multiple threads write to OutputStream concurrently, but that doesn't happen in most of the situations, so I'm puzzled. For the time being, cheating by just suppressing the assertion. HUDSON-7581 appears to be related. */ // if (available<0) // throw new AssertionError(); } } private static final Logger LOGGER = Logger.getLogger(PipeWindow.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/RemoteClassLoader.java0000644000175000017500000005453112122627370027402 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.net.URL; import java.net.MalformedURLException; import java.net.URLClassLoader; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Vector; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import java.util.logging.Level; import java.util.logging.Logger; /** * Loads class files from the other peer through {@link Channel}. * *

* If the {@linkplain Channel#isRestricted() channel is restricted}, this classloader will be * created by will not attempt to load anything from the remote classloader. The reason we * create such a useless instance is so that when such classloader is sent back to the remote side again, * the remoting system can re-discover what {@link ClassLoader} this was tied to. * * @author Kohsuke Kawaguchi */ final class RemoteClassLoader extends URLClassLoader { private static final Logger LOGGER = Logger.getLogger(RemoteClassLoader.class.getName()); /** * Proxy to the code running on remote end. */ private final IClassLoader proxy; /** * Remote peer that the {@link #proxy} is connected to. */ private final Channel channel; private final Map resourceMap = new HashMap(); private final Map> resourcesMap = new HashMap>(); /** * List of jars that are already pre-fetched through {@link #addURL(URL)}. * *

* Note that URLs in this set are URLs on the other peer. */ private final Set prefetchedJars = new HashSet(); public static ClassLoader create(ClassLoader parent, IClassLoader proxy) { if(proxy instanceof ClassLoaderProxy) { // when the remote sends 'RemoteIClassLoader' as the proxy, on this side we get it // as ClassLoaderProxy. This means, the so-called remote classloader here is // actually our classloader that we exported to the other side. return ((ClassLoaderProxy)proxy).cl; } return new RemoteClassLoader(parent, proxy); } private RemoteClassLoader(ClassLoader parent, IClassLoader proxy) { super(new URL[0],parent); this.proxy = proxy; this.channel = RemoteInvocationHandler.unwrap(proxy); } /** * If this {@link RemoteClassLoader} represents a classloader from the specified channel, * return its exported OID. Otherwise return -1. */ /*package*/ int getOid(Channel channel) { return RemoteInvocationHandler.unwrap(proxy,channel); } protected Class findClass(String name) throws ClassNotFoundException { try { // first attempt to load from locally fetched jars return super.findClass(name); } catch (ClassNotFoundException e) { if(channel.isRestricted()) throw e; // delegate to remote if (channel.remoteCapability.supportsMultiClassLoaderRPC()) { /* In multi-classloader setup, RemoteClassLoaders do not retain the relationships among the original classloaders, so each RemoteClassLoader ends up loading classes on its own without delegating to other RemoteClassLoaders. See the classloader X/Y examples in HUDSON-5048 for the depiction of the problem. So instead, we find the right RemoteClassLoader to load the class on per class basis. The communication is optimized for the single classloader use, by always returning the class file image along with the reference to the initiating ClassLoader (if the initiating ClassLoader has already loaded this class, then the class file image is wasted.) */ long startTime = System.nanoTime(); ClassFile cf = proxy.fetch2(name); channel.classLoadingTime.addAndGet(System.nanoTime()-startTime); channel.classLoadingCount.incrementAndGet(); ClassLoader cl = channel.importedClassLoaders.get(cf.classLoader); if (cl instanceof RemoteClassLoader) { RemoteClassLoader rcl = (RemoteClassLoader) cl; synchronized (_getClassLoadingLock(rcl, name)) { Class c = rcl.findLoadedClass(name); if (TESTING) { try { Thread.sleep(1000); } catch (InterruptedException x) { assert false : x; } } if (c==null) c = rcl.loadClassFile(name,cf.classImage); return c; } } else { return cl.loadClass(name); } } else { long startTime = System.nanoTime(); byte[] bytes = proxy.fetch(name); channel.classLoadingTime.addAndGet(System.nanoTime()-startTime); channel.classLoadingCount.incrementAndGet(); return loadClassFile(name, bytes); } } } private static Method gCLL; static { try { gCLL = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class); gCLL.setAccessible(true); } catch (NoSuchMethodException x) { // OK, Java 6 } catch (Exception x) { LOGGER.log(Level.WARNING, null, x); } } private static Object _getClassLoadingLock(RemoteClassLoader rcl, String name) { // Java 7: return rcl.getClassLoadingLock(name); if (gCLL != null) { try { return gCLL.invoke(rcl, name); } catch (Exception x) { LOGGER.log(Level.WARNING, null, x); } } return rcl; } /** JENKINS-6604 */ static boolean TESTING; private Class loadClassFile(String name, byte[] bytes) { // define package definePackage(name); try { return defineClass(name, bytes, 0, bytes.length); } catch (ClassFormatError e) { throw (ClassFormatError)new ClassFormatError("Failed to load "+name).initCause(e); } } /** * Defining a package is necessary to make {@link Class#getPackage()} work, * which is often used to retrieve package-level annotations. * (for example, JAXB RI and Hadoop use them.) */ private void definePackage(String name) { int idx = name.lastIndexOf('.'); if (idx<0) return; // not in a package String packageName = name.substring(0,idx); if (getPackage(packageName) != null) // already defined return; definePackage(packageName, null, null, null, null, null, null, null); } public URL findResource(String name) { // first attempt to load from locally fetched jars URL url = super.findResource(name); if(url!=null || channel.isRestricted()) return url; try { if(resourceMap.containsKey(name)) { File f = resourceMap.get(name); if(f==null) return null; // no such resource if(f.exists()) // be defensive against external factors that might have deleted this file, since we use /tmp // see http://www.nabble.com/Surefire-reports-tt17554215.html return f.toURI().toURL(); } long startTime = System.nanoTime(); byte[] image = proxy.getResource(name); channel.resourceLoadingTime.addAndGet(System.nanoTime()-startTime); channel.resourceLoadingCount.incrementAndGet(); if(image==null) { resourceMap.put(name,null); return null; } File res = makeResource(name, image); resourceMap.put(name,res); return res.toURI().toURL(); } catch (IOException e) { throw new Error("Unable to load resource "+name,e); } } private static Vector toURLs(Vector files) throws MalformedURLException { Vector r = new Vector(files.size()); for (File f : files) { if(!f.exists()) return null; // abort r.add(f.toURI().toURL()); } return r; } public Enumeration findResources(String name) throws IOException { if(channel.isRestricted()) return new Vector().elements(); // TODO: use the locally fetched jars to speed up the look up // the challenge is how to combine the list from local jars // and the remote list Vector files = resourcesMap.get(name); if(files!=null) { Vector urls = toURLs(files); if(urls!=null) return urls.elements(); } long startTime = System.nanoTime(); byte[][] images = proxy.getResources(name); channel.resourceLoadingTime.addAndGet(System.nanoTime()-startTime); channel.resourceLoadingCount.incrementAndGet(); files = new Vector(); for( byte[] image: images ) files.add(makeResource(name,image)); resourcesMap.put(name,files); return toURLs(files).elements(); } // FIXME move to utils /** Instructs Java to recursively delete the given directory (dir) and its contents when the JVM exits. * @param dir File customer representing directory to delete. If this file argument is not a directory, it will still * be deleted.

* The method works in Java 1.3, Java 1.4, Java 5.0 and Java 6.0; but it does not work with some early Java 6.0 versions * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6437591 */ public static void deleteDirectoryOnExit(final File dir) { // Delete this on exit. Delete on exit requests are processed in REVERSE order dir.deleteOnExit(); // If it's a directory, visit its children. This recursive walk has to be done AFTER calling deleteOnExit // on the directory itself because Java deletes the files to be deleted on exit in reverse order. if (dir.isDirectory()) { File[] childFiles = dir.listFiles(); if (childFiles != null) { // listFiles may return null if there's an IO error for (File f: childFiles) { deleteDirectoryOnExit(f); } } } } private File makeResource(String name, byte[] image) throws IOException { File tmpFile = createTempDir(); File resource = new File(tmpFile, name); resource.getParentFile().mkdirs(); FileOutputStream fos = new FileOutputStream(resource); fos.write(image); fos.close(); deleteDirectoryOnExit(tmpFile); return resource; } private File createTempDir() throws IOException { // work around sun bug 6325169 on windows // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6325169 int nRetry=0; while (true) { try { File tmpFile = File.createTempFile("hudson-remoting", ""); tmpFile.delete(); tmpFile.mkdir(); return tmpFile; } catch (IOException e) { if (nRetry++ < 100){ continue; } IOException nioe = new IOException("failed to create temp directory at default location, most probably at: "+System.getProperty("java.io.tmpdir")); nioe.initCause(e); throw nioe; } } } /** * Prefetches the jar into this class loader. * * @param jar * Jar to be prefetched. Note that this file is an file on the other end, * and doesn't point to anything meaningful locally. * @return * true if the prefetch happened. false if the jar is already prefetched. * @see Channel#preloadJar(Callable, Class[]) */ /*package*/ boolean prefetch(URL jar) throws IOException { synchronized (prefetchedJars) { if(prefetchedJars.contains(jar)) return false; String p = jar.getPath().replace('\\','/'); p = p.substring(p.lastIndexOf('/')+1); File localJar = makeResource(p,proxy.fetchJar(jar)); addURL(localJar.toURI().toURL()); prefetchedJars.add(jar); return true; } } public static class ClassFile implements Serializable { /** * oid of the classloader that should load this class. */ final int classLoader; final byte[] classImage; ClassFile(int classLoader, byte[] classImage) { this.classLoader = classLoader; this.classImage = classImage; } private static final long serialVersionUID = 1L; } /** * Remoting interface. */ public static interface IClassLoader { byte[] fetchJar(URL url) throws IOException; byte[] fetch(String className) throws ClassNotFoundException; ClassFile fetch2(String className) throws ClassNotFoundException; byte[] getResource(String name) throws IOException; byte[][] getResources(String name) throws IOException; } public static IClassLoader export(ClassLoader cl, Channel local) { if (cl instanceof RemoteClassLoader) { // check if this is a remote classloader from the channel final RemoteClassLoader rcl = (RemoteClassLoader) cl; int oid = RemoteInvocationHandler.unwrap(rcl.proxy, local); if(oid!=-1) { return new RemoteIClassLoader(oid,rcl.proxy); } } return local.export(IClassLoader.class, new ClassLoaderProxy(cl,local), false); } public static void pin(ClassLoader cl, Channel local) { if (cl instanceof RemoteClassLoader) { // check if this is a remote classloader from the channel final RemoteClassLoader rcl = (RemoteClassLoader) cl; int oid = RemoteInvocationHandler.unwrap(rcl.proxy, local); if(oid!=-1) return; } local.pin(new ClassLoaderProxy(cl,local)); } /** * Exports and just returns the object ID, instead of obtaining the proxy. */ static int exportId(ClassLoader cl, Channel local) { return local.export(new ClassLoaderProxy(cl,local), false); } /*package*/ static final class ClassLoaderProxy implements IClassLoader { final ClassLoader cl; final Channel channel; public ClassLoaderProxy(ClassLoader cl, Channel channel) { assert cl != null; this.cl = cl; this.channel = channel; } public byte[] fetchJar(URL url) throws IOException { return readFully(url.openStream()); } public byte[] fetch(String className) throws ClassNotFoundException { if (!USE_BOOTSTRAP_CLASSLOADER && cl==PSEUDO_BOOTSTRAP) { throw new ClassNotFoundException("Classloading from bootstrap classloader disabled"); } InputStream in = cl.getResourceAsStream(className.replace('.', '/') + ".class"); if(in==null) throw new ClassNotFoundException(className); try { return readFully(in); } catch (IOException e) { throw new ClassNotFoundException(); } } public ClassFile fetch2(String className) throws ClassNotFoundException { ClassLoader ecl = cl.loadClass(className).getClassLoader(); if (ecl == null) { if (USE_BOOTSTRAP_CLASSLOADER) { ecl = PSEUDO_BOOTSTRAP; } else { throw new ClassNotFoundException("Classloading from system classloader disabled"); } } try { InputStream in = ecl.getResourceAsStream(className.replace('.', '/') + ".class"); if(in==null) throw new ClassNotFoundException(className); return new ClassFile( exportId(ecl,channel), readFully(in)); } catch (IOException e) { throw new ClassNotFoundException(); } } public byte[] getResource(String name) throws IOException { URL resource = cl.getResource(name); if (resource == null) { return null; } if (!USE_BOOTSTRAP_CLASSLOADER) { URL systemResource = PSEUDO_BOOTSTRAP.getResource(name); if (resource.equals(systemResource)) { return null; } } return readFully(resource.openStream()); } public byte[][] getResources(String name) throws IOException { List images = new ArrayList(); Set systemResources = null; if (!USE_BOOTSTRAP_CLASSLOADER) { systemResources = new HashSet(); Enumeration e = PSEUDO_BOOTSTRAP.getResources(name); while (e.hasMoreElements()) { systemResources.add(e.nextElement()); } } Enumeration e = cl.getResources(name); while(e.hasMoreElements()) { URL url = e.nextElement(); if (systemResources == null || !systemResources.contains(url)) { images.add(readFully(url.openStream())); } } return images.toArray(new byte[images.size()][]); } private byte[] readFully(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[8192]; int len; while((len=in.read(buf))>0) baos.write(buf,0,len); in.close(); return baos.toByteArray(); } public boolean equals(Object that) { if (this == that) return true; if (that == null || getClass() != that.getClass()) return false; return cl.equals(((ClassLoaderProxy) that).cl); } public int hashCode() { return cl.hashCode(); } @Override public String toString() { return super.toString()+'['+cl.toString()+']'; } /** * Since bootstrap classloader by itself doesn't have the {@link ClassLoader} object * representing it (a crazy design, really), accessing it is unnecessarily hard. * *

* So we create a child classloader that delegates directly to the bootstrap, without adding * any new classpath. In this way, we can effectively use this classloader as a representation * of the bootstrap classloader. */ private static final ClassLoader PSEUDO_BOOTSTRAP = new URLClassLoader(new URL[0],null) { @Override public String toString() { return "PSEUDO_BOOTSTRAP"; } }; } /** * {@link IClassLoader} to be shipped back to the channel where it came from. * *

* When the object stays on the side where it's created, delegate to the proxy field * to work (which will be the remote instance.) Once transferred to the other side, * resolve back to the instance on the server. */ private static class RemoteIClassLoader implements IClassLoader, Serializable { private transient final IClassLoader proxy; private final int oid; private RemoteIClassLoader(int oid, IClassLoader proxy) { this.proxy = proxy; this.oid = oid; } public byte[] fetchJar(URL url) throws IOException { return proxy.fetchJar(url); } public byte[] fetch(String className) throws ClassNotFoundException { return proxy.fetch(className); } public ClassFile fetch2(String className) throws ClassNotFoundException { return proxy.fetch2(className); } public byte[] getResource(String name) throws IOException { return proxy.getResource(name); } public byte[][] getResources(String name) throws IOException { return proxy.getResources(name); } private Object readResolve() { return Channel.current().getExportedObject(oid); } private static final long serialVersionUID = 1L; } /** * If set to true, classes loaded by the bootstrap classloader will be also remoted to the remote JVM. * By default, classes that belong to the bootstrap classloader will NOT be remoted, as each JVM gets its own JRE * and their versions can be potentially different. */ public static boolean USE_BOOTSTRAP_CLASSLOADER = Boolean.getBoolean(RemoteClassLoader.class.getName() + ".useBootstrapClassLoader"); } jenkins-remoting-2.23/src/main/java/hudson/remoting/FutureAdapter.java0000644000175000017500000000423312122627370026577 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * {@link Future} that converts the return type. * * @author Kohsuke Kawaguchi */ abstract class FutureAdapter implements Future { protected final Future core; protected FutureAdapter(Future core) { this.core = core; } public boolean cancel(boolean mayInterruptIfRunning) { return core.cancel(mayInterruptIfRunning); } public boolean isCancelled() { return core.isCancelled(); } public boolean isDone() { return core.isDone(); } public X get() throws InterruptedException, ExecutionException { return adapt(core.get()); } public X get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return adapt(core.get(timeout, unit)); } protected abstract X adapt(Y y) throws ExecutionException; } jenkins-remoting-2.23/src/main/java/hudson/remoting/Future.java0000644000175000017500000000267112122627370025302 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * Alias to {@link Future}. * *

* This alias is defined so that retro-translation won't affect * the publicly committed signature of the API. * * @author Kohsuke Kawaguchi */ public interface Future extends java.util.concurrent.Future { } jenkins-remoting-2.23/src/main/java/hudson/remoting/IReadResolve.java0000644000175000017500000000266712122627370026361 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.ObjectStreamException; /** * Used internally in the remoting code to have the proxy object * implement readResolve. * * @author Kohsuke Kawaguchi */ public interface IReadResolve { Object readResolve() throws ObjectStreamException; } jenkins-remoting-2.23/src/main/java/hudson/remoting/SocketInputStream.java0000644000175000017500000000370112122627370027447 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; /** * {@link InputStream} connected to socket. * *

* Unlike plain {@link Socket#getInputStream()}, closing the stream * does not close the entire socket, and instead it merely partial-close * a socket in the direction. * * @author Kohsuke Kawaguchi */ public class SocketInputStream extends FilterInputStream { private final Socket socket; public SocketInputStream(Socket socket) throws IOException { super(socket.getInputStream()); this.socket = socket; } @Override public void close() throws IOException { socket.shutdownInput(); if (socket.isOutputShutdown()) socket.close(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/forward/0000755000175000017500000000000012122627370024623 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/java/hudson/remoting/forward/ForwarderFactory.java0000644000175000017500000000613712122627370030760 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.forward; import hudson.remoting.VirtualChannel; import hudson.remoting.Callable; import hudson.remoting.SocketInputStream; import hudson.remoting.RemoteOutputStream; import hudson.remoting.SocketOutputStream; import hudson.remoting.Channel; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; /** * Creates {@link Forwarder}. * * @author Kohsuke Kawaguchi */ public class ForwarderFactory { /** * Creates a connector on the remote side that connects to the speicied host and port. */ public static Forwarder create(VirtualChannel channel, final String remoteHost, final int remotePort) throws IOException, InterruptedException { return channel.call(new Callable() { public Forwarder call() throws IOException { return new ForwarderImpl(remoteHost,remotePort); } private static final long serialVersionUID = 1L; }); } public static Forwarder create(String remoteHost, int remotePort) { return new ForwarderImpl(remoteHost,remotePort); } private static class ForwarderImpl implements Forwarder { private final String remoteHost; private final int remotePort; private ForwarderImpl(String remoteHost, int remotePort) { this.remoteHost = remoteHost; this.remotePort = remotePort; } public OutputStream connect(OutputStream out) throws IOException { Socket s = new Socket(remoteHost, remotePort); new CopyThread(String.format("Copier to %s:%d", remoteHost, remotePort), new SocketInputStream(s), out).start(); return new RemoteOutputStream(new SocketOutputStream(s)); } /** * When sent to the remote node, send a proxy. */ private Object writeReplace() { return Channel.current().export(Forwarder.class, this); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/forward/PortForwarder.java0000644000175000017500000001102612122627370030266 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.forward; import hudson.remoting.RemoteOutputStream; import hudson.remoting.SocketOutputStream; import hudson.remoting.SocketInputStream; import hudson.remoting.VirtualChannel; import hudson.remoting.Callable; import hudson.remoting.Channel; import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; import java.io.OutputStream; import java.io.Closeable; import static java.util.logging.Level.FINE; import java.util.logging.Logger; /** * Port forwarder over a remote channel. * * @author Kohsuke Kawaguchi * @since 1.315 */ public class PortForwarder extends Thread implements Closeable, ListeningPort { private final Forwarder forwarder; private final ServerSocket socket; public PortForwarder(int localPort, Forwarder forwarder) throws IOException { super(String.format("Port forwarder %d",localPort)); this.forwarder = forwarder; this.socket = new ServerSocket(localPort); // mark as a daemon thread by default. // the caller can explicitly cancel this by doing "setDaemon(false)" setDaemon(true); } public int getPort() { return socket.getLocalPort(); } @Override public void run() { try { try { while(true) { final Socket s = socket.accept(); new Thread("Port forwarding session from "+s.getRemoteSocketAddress()) { public void run() { try { final OutputStream out = forwarder.connect(new RemoteOutputStream(new SocketOutputStream(s))); new CopyThread("Copier for "+s.getRemoteSocketAddress(), new SocketInputStream(s), out).start(); } catch (IOException e) { // this happens if the socket connection is terminated abruptly. LOGGER.log(FINE,"Port forwarding session was shut down abnormally",e); } } }.start(); } } finally { socket.close(); } } catch (IOException e) { LOGGER.log(FINE,"Port forwarding was shut down abnormally",e); } } /** * Shuts down this port forwarder. */ public void close() throws IOException { interrupt(); socket.close(); } /** * Starts a {@link PortForwarder} accepting remotely at the given channel, * which connects by using the given connector. * * @return * A {@link Closeable} that can be used to shut the port forwarding down. */ public static ListeningPort create(VirtualChannel ch, final int acceptingPort, Forwarder forwarder) throws IOException, InterruptedException { // need a remotable reference final Forwarder proxy = ch.export(Forwarder.class, forwarder); return ch.call(new Callable() { public ListeningPort call() throws IOException { PortForwarder t = new PortForwarder(acceptingPort, proxy); t.start(); return Channel.current().export(ListeningPort.class,t); } }); } private static final Logger LOGGER = Logger.getLogger(PortForwarder.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/forward/Forwarder.java0000644000175000017500000000330212122627370027417 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting.forward; import java.io.Serializable; import java.io.OutputStream; import java.io.IOException; /** * Abstracts away how the forwarding is set up. * * @author Kohsuke Kawaguchi */ public interface Forwarder extends Serializable { /** * Establishes a port forwarding connection and returns * the writer end. * * @param out * The writer end to the initiator. The callee will * start a thread that writes to this. */ OutputStream connect(OutputStream out) throws IOException; } jenkins-remoting-2.23/src/main/java/hudson/remoting/forward/ListeningPort.java0000644000175000017500000000105312122627370030266 0ustar jamespagejamespagepackage hudson.remoting.forward; import java.io.Closeable; import java.io.IOException; /** * Represents a listening port that forwards a connection * via port forwarding. * * @author Kohsuke Kawaguchi */ public interface ListeningPort extends Closeable { /** * TCP/IP port that is listening. */ int getPort(); /** * Shuts down the port forwarding by removing the server socket. * Connections that are already established will not be affected * by this operation. */ void close() throws IOException; } jenkins-remoting-2.23/src/main/java/hudson/remoting/forward/CopyThread.java0000644000175000017500000000207112122627370027530 0ustar jamespagejamespagepackage hudson.remoting.forward; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; /** * Copies a stream and close them at EOF. * * @author Kohsuke Kawaguchi */ final class CopyThread extends Thread { private static final Logger LOGGER = Logger.getLogger(CopyThread.class.getName()); private final InputStream in; private final OutputStream out; public CopyThread(String threadName, InputStream in, OutputStream out) { super(threadName); this.in = in; this.out = out; } public void run() { try { try { byte[] buf = new byte[8192]; int len; while ((len = in.read(buf)) > 0) out.write(buf, 0, len); } finally { in.close(); out.close(); } } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception while copying in thread: " + getName(), e); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/forward/package-info.java0000644000175000017500000000013612122627370030012 0ustar jamespagejamespage/** * TCP port forwarding over {@code hudson.remoting}. */ package hudson.remoting.forward; jenkins-remoting-2.23/src/main/java/hudson/remoting/Capability.java0000644000175000017500000000751512122627370026113 0ustar jamespagejamespagepackage hudson.remoting; import hudson.remoting.Channel.Mode; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; /** * Represents additional features implemented on {@link Channel}. * *

* Each {@link Channel} exposes its capability to {@link Channel#getProperty(Object)}. * *

* This mechanism allows two different versions of remoting.jar to talk to each other. * * @author Kohsuke Kawaguchi * @see Channel#remoteCapability */ public final class Capability implements Serializable { /** * Bit mask of optional capabilities. */ private final long mask; Capability(long mask) { this.mask = mask; } public Capability() { this(MASK_MULTI_CLASSLOADER|MASK_PIPE_THROTTLING|MASK_MIMIC_EXCEPTION); } /** * Does this implementation supports multi-classloader serialization in * {@link UserRequest}? * * @see MultiClassLoaderSerializer */ public boolean supportsMultiClassLoaderRPC() { return (mask&MASK_MULTI_CLASSLOADER)!=0; } /** * Does the implementation supports window size control over pipes? * * @see ProxyOutputStream */ public boolean supportsPipeThrottling() { return (mask& MASK_PIPE_THROTTLING)!=0; } public boolean hasMimicException() { return (mask&MASK_MIMIC_EXCEPTION)!=0; } /** * Writes out the capacity preamble. */ void writePreamble(OutputStream os) throws IOException { os.write(PREAMBLE); ObjectOutputStream oos = new ObjectOutputStream(Mode.TEXT.wrap(os)); oos.writeObject(this); oos.flush(); } /** * The opposite operation of {@link #writePreamble(OutputStream)}. */ public static Capability read(InputStream is) throws IOException { try { ObjectInputStream ois = new ObjectInputStream(Mode.TEXT.wrap(is)); return (Capability)ois.readObject(); } catch (ClassNotFoundException e) { throw (Error)new NoClassDefFoundError(e.getMessage()).initCause(e); } } private static final long serialVersionUID = 1L; /** * This was used briefly to indicate the use of {@link MultiClassLoaderSerializer}, but * that was disabled (see HUDSON-4293) in Sep 2009. AFAIK no released version of Hudson * exposed it, but since then the wire format of {@link MultiClassLoaderSerializer} has evolved * in an incompatible way. *

* So just to be on the safe side, I assigned a different bit to indicate this feature {@link #MASK_MULTI_CLASSLOADER}, * so that even if there are remoting.jar out there that advertizes this bit, we won't be using * the new {@link MultiClassLoaderSerializer} code. *

* If we ever use up all 64bits of long, we can probably come back and reuse this bit, as by then * hopefully any such remoting.jar deployment is long gone. */ private static final long MASK_UNUSED1 = 1L; /** * Bit that indicates the use of {@link MultiClassLoaderSerializer}. */ private static final long MASK_MULTI_CLASSLOADER = 2L; /** * Bit that indicates the use of TCP-like window control for {@link ProxyOutputStream}. */ private static final long MASK_PIPE_THROTTLING = 4L; /** * Supports {@link MimicException}. */ private static final long MASK_MIMIC_EXCEPTION = 8L; static final byte[] PREAMBLE; public static final Capability NONE = new Capability(0); static { try { PREAMBLE = "<===[JENKINS REMOTING CAPACITY]===>".getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } } jenkins-remoting-2.23/src/main/java/hudson/remoting/ChannelClosedException.java0000644000175000017500000000100512122627370030377 0ustar jamespagejamespagepackage hudson.remoting; import java.io.IOException; /** * Indicates that the channel is already closed. * * @author Kohsuke Kawaguchi */ public class ChannelClosedException extends IOException { /** * @deprecated * Use {@link #ChannelClosedException(Throwable)}. */ public ChannelClosedException() { super("channel is already closed"); } public ChannelClosedException(Throwable cause) { super("channel is already closed"); initCause(cause); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/PingThread.java0000644000175000017500000001200112122627370026041 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.util.concurrent.ExecutionException; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; /** * Periodically perform a ping. * *

* Useful when a connection needs to be kept alive by sending data, * or when the disconnection is not properly detected. * *

* {@link #onDead()} method needs to be overrided to define * what to do when a connection appears to be dead. * * @author Kohsuke Kawaguchi * @since 1.170 */ public abstract class PingThread extends Thread { private final Channel channel; /** * Time out in milliseconds. * If the response doesn't come back by then, the channel is considered dead. */ private final long timeout; /** * Performs a check every this milliseconds. */ private final long interval; public PingThread(Channel channel, long timeout, long interval) { super("Ping thread for channel "+channel); this.channel = channel; this.timeout = timeout; this.interval = interval; setDaemon(true); } public PingThread(Channel channel, long interval) { this(channel, 4*60*1000/*4 mins*/, interval); } public PingThread(Channel channel) { this(channel,10*60*1000/*10 mins*/); } public void run() { try { while(true) { long nextCheck = System.currentTimeMillis()+interval; ping(); // wait until the next check long diff; while((diff=nextCheck-System.currentTimeMillis())>0) Thread.sleep(diff); } } catch (ChannelClosedException e) { LOGGER.fine(getName()+" is closed. Terminating"); } catch (IOException e) { onDead(e); } catch (InterruptedException e) { // use interruption as a way to terminate the ping thread. LOGGER.fine(getName()+" is interrupted. Terminating"); } } private void ping() throws IOException, InterruptedException { Future f = channel.callAsync(new Ping()); long start = System.currentTimeMillis(); long end = start +timeout; long remaining; do { remaining = end-System.currentTimeMillis(); try { f.get(Math.max(0,remaining),MILLISECONDS); return; } catch (ExecutionException e) { if (e.getCause() instanceof RequestAbortedException) return; // connection has shut down orderly. onDead(e); return; } catch (TimeoutException e) { // get method waits "at most the amount specified in the timeout", // so let's make sure that it really waited enough } } while(remaining>0); onDead(new TimeoutException("Ping started on "+start+" hasn't completed at "+System.currentTimeMillis()));//.initCause(e) } /** * Called when ping failed. * * @deprecated as of 2.9 * Override {@link #onDead(Throwable)} to receive the cause, but also override this method * and provide a fallback behaviour to be backward compatible with earlier version of remoting library. */ protected abstract void onDead(); /** * Called when ping failed. * * @since 2.9 */ protected void onDead(Throwable diagnosis) { onDead(); // fall back } private static final class Ping implements Callable { private static final long serialVersionUID = 1L; public Void call() throws IOException { return null; } } private static final Logger LOGGER = Logger.getLogger(PingThread.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/package-info.java0000644000175000017500000000256612122627370026357 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * Remoting infrastructure for Hudson. * *

* Code in this package is used for running a part of a program in slaves. * If you are new to this package, start from {@link Channel}. */ package hudson.remoting; jenkins-remoting-2.23/src/main/java/hudson/remoting/UserRequest.java0000644000175000017500000002046412122627370026317 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.RemoteClassLoader.IClassLoader; import hudson.remoting.ExportTable.ExportList; import hudson.remoting.RemoteInvocationHandler.RPCRequest; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.NotSerializableException; import java.io.ObjectInputStream; /** * {@link Request} that can take {@link Callable} whose actual implementation * may not be known to the remote system in advance. * *

* This code assumes that the {@link Callable} object and all reachable code * are loaded by a single classloader. * * @author Kohsuke Kawaguchi */ final class UserRequest extends Request,EXC> { private final byte[] request; private final IClassLoader classLoaderProxy; private final String toString; /** * Objects exported by the request. This value will remain local * and won't be sent over to the remote side. */ private transient final ExportList exports; public UserRequest(Channel local, Callable c) throws IOException { exports = local.startExportRecording(); try { request = serialize(c,local); } finally { exports.stopRecording(); } this.toString = c.toString(); ClassLoader cl = getClassLoader(c); classLoaderProxy = RemoteClassLoader.export(cl,local); } /*package*/ static ClassLoader getClassLoader(Callable c) { ClassLoader result = null; if(c instanceof DelegatingCallable) { result =((DelegatingCallable)c).getClassLoader(); } else { result = c.getClass().getClassLoader(); } if (result == null) { result = ClassLoader.getSystemClassLoader(); } return result; } protected UserResponse perform(Channel channel) throws EXC { try { ClassLoader cl = channel.importedClassLoaders.get(classLoaderProxy); RSP r = null; Channel oldc = Channel.setCurrent(channel); try { Object o; try { o = deserialize(channel,request,cl); } catch (ClassNotFoundException e) { throw new ClassNotFoundException("Failed to deserialize the Callable object. Perhaps you needed to implement DelegatingCallable?",e); } catch (RuntimeException e) { // if the error is during deserialization, throw it in one of the types Channel.call will // capture its call site stack trace. See throw new Error("Failed to deserialize the Callable object.",e); } Callable callable = (Callable)o; if(channel.isRestricted() && !(callable instanceof RPCRequest)) // if we allow restricted channel to execute arbitrary Callable, the remote JVM can pick up many existing // Callable implementations (such as ones in Hudson's FilePath) and do quite a lot. So restrict that. // OTOH, we need to allow RPCRequest so that method invocations on exported objects will go through. throw new SecurityException("Execution of "+callable.toString()+" is prohibited because the channel is restricted"); ClassLoader old = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(cl); // execute the service try { r = callable.call(); } finally { Thread.currentThread().setContextClassLoader(old); } } finally { Channel.setCurrent(oldc); } return new UserResponse(serialize(r,channel),false); } catch (Throwable e) { // propagate this to the calling process try { byte[] response; try { response = _serialize(e, channel); } catch (NotSerializableException x) { // perhaps the thrown runtime exception is of type we can't handle response = serialize(new ProxyException(e), channel); } return new UserResponse(response,true); } catch (IOException x) { // throw it as a lower-level exception throw (EXC)x; } } } private byte[] _serialize(Object o, final Channel channel) throws IOException { Channel old = Channel.setCurrent(channel); try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos; if (channel.remoteCapability.supportsMultiClassLoaderRPC()) oos = new MultiClassLoaderSerializer.Output(channel,baos); else oos = new ObjectOutputStream(baos); oos.writeObject(o); return baos.toByteArray(); } finally { Channel.setCurrent(old); } } private byte[] serialize(Object o, Channel localChannel) throws IOException { try { return _serialize(o,localChannel); } catch( NotSerializableException e ) { IOException x = new IOException("Unable to serialize " + o); x.initCause(e); throw x; } } /*package*/ static Object deserialize(final Channel channel, byte[] data, ClassLoader defaultClassLoader) throws IOException, ClassNotFoundException { ByteArrayInputStream in = new ByteArrayInputStream(data); ObjectInputStream ois; if (channel.remoteCapability.supportsMultiClassLoaderRPC()) { // this code is coupled with the ObjectOutputStream subtype above ois = new MultiClassLoaderSerializer.Input(channel, in); } else { ois = new ObjectInputStreamEx(in, defaultClassLoader); } return ois.readObject(); } public void releaseExports() { exports.release(); } public String toString() { return "UserRequest:"+toString; } private static final long serialVersionUID = 1L; } final class UserResponse implements Serializable { private final byte[] response; private final boolean isException; public UserResponse(byte[] response, boolean isException) { this.response = response; this.isException = isException; } /** * Deserializes the response byte stream into an object. */ public RSP retrieve(Channel channel, ClassLoader cl) throws IOException, ClassNotFoundException, EXC { Channel old = Channel.setCurrent(channel); try { Object o = UserRequest.deserialize(channel,response,cl); if(isException) throw (EXC)o; else return (RSP) o; } finally { Channel.setCurrent(old); } } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/AbstractByteArrayCommandTransport.java0000644000175000017500000000604112122627370032625 0ustar jamespagejamespagepackage hudson.remoting; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.logging.Level; import java.util.logging.Logger; /** * {@link CommandTransport} that works with {@code byte[]} instead of command object. * * This base class hides away some of the {@link Command} serialization details. One less thing * for transport implementers to worry about. * * @author Kohsuke Kawaguchi * @since 2.13 */ public abstract class AbstractByteArrayCommandTransport extends CommandTransport { protected Channel channel; /** * Writes a byte[] to the transport. * * The block boundary is significant. A transport needs to ensure that that the same byte[] is * read by the peer (unlike TCP, where a single write can * be split into multiple read()s on the other side.) */ public abstract void writeBlock(Channel channel, byte[] payload) throws IOException; /** * Starts the transport. * * See {@link #setup(Channel, CommandReceiver)} for more details. * * In this subtype, we pass in {@link ByteArrayReceiver} that uses byte[] instead of {@link Command} */ public abstract void setup(ByteArrayReceiver receiver); public static interface ByteArrayReceiver { /** * Notifies the {@link Channel} that the transport has received a new block. * * As discussed in {@link AbstractByteArrayCommandTransport#writeBlock(Channel, byte[])}, * the block boundary is significant. */ void handle(byte[] payload); /** * See {@link CommandReceiver#handle(Command)} for details. */ void terminate(IOException e); } @Override public final void setup(final Channel channel, final CommandReceiver receiver) { this.channel = channel; setup(new ByteArrayReceiver() { public void handle(byte[] payload) { try { receiver.handle(Command.readFrom(channel, new ObjectInputStreamEx( new ByteArrayInputStream(payload), channel.baseClassLoader))); } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to construct Command", e); } catch (ClassNotFoundException e) { LOGGER.log(Level.WARNING, "Failed to construct Command", e); } } public void terminate(IOException e) { receiver.terminate(e); } }); } @Override public final void write(Command cmd, boolean last) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); cmd.writeTo(channel,oos); oos.close(); writeBlock(channel,baos.toByteArray()); } private static final Logger LOGGER = Logger.getLogger(AbstractByteArrayCommandTransport.class.getName()); } jenkins-remoting-2.23/src/main/java/hudson/remoting/ChannelProperty.java0000644000175000017500000000321012122627370027133 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2010, InfraDNA, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.Serializable; /** * A convenient key type for {@link Channel#getProperty(Object)} and {@link Channel#setProperty(Object, Object)} * * @author Kohsuke Kawaguchi */ public class ChannelProperty implements Serializable { public final Class type; public final String displayName; public ChannelProperty(Class type, String displayName) { this.type = type; this.displayName = displayName; } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/ClassLoaderHolder.java0000644000175000017500000000256012122627370027357 0ustar jamespagejamespagepackage hudson.remoting; import hudson.remoting.RemoteClassLoader.IClassLoader; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * Remoting-aware holder of {@link ClassLoader} that replaces ClassLoader by its {@link RemoteClassLoader}. * * @author Kohsuke Kawaguchi * @since 2.12 */ public class ClassLoaderHolder implements Serializable { private transient ClassLoader classLoader; public ClassLoaderHolder(ClassLoader classLoader) { this.classLoader = classLoader; } public ClassLoaderHolder() { } public ClassLoader get() { return classLoader; } public void set(ClassLoader classLoader) { this.classLoader = classLoader; } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { IClassLoader proxy = (IClassLoader)ois.readObject(); classLoader = proxy==null ? null : Channel.current().importedClassLoaders.get(proxy); } private void writeObject(ObjectOutputStream oos) throws IOException { if (classLoader==null) oos.writeObject(null); else { IClassLoader proxy = RemoteClassLoader.export(classLoader, Channel.current()); oos.writeObject(proxy); } } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/RemoteWriter.java0000644000175000017500000000711312122627370026454 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.Writer; /** * {@link Writer} that can be sent over to the remote {@link Channel}, * so that the remote {@link Callable} can write to a local {@link Writer}. * *

Usage

*
 * final Writer out = new RemoteWriter(w);
 *
 * channel.call(new Callable() {
 *   public Object call() {
 *     // this will write to 'w'.
 *     out.write(...);
 *   }
 * });
 * 
* * @see RemoteInputStream * @author Kohsuke Kawaguchi */ public final class RemoteWriter extends Writer implements Serializable { /** * On local machine, this points to the {@link Writer} where * the data will be sent ultimately. * * On remote machine, this points to {@link ProxyOutputStream} that * does the network proxy. */ private transient Writer core; public RemoteWriter(Writer core) { this.core = core; } private void writeObject(ObjectOutputStream oos) throws IOException { int id = Channel.current().export(core); oos.writeInt(id); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { final Channel channel = Channel.current(); assert channel !=null; this.core = new ProxyWriter(channel, ois.readInt()); } private static final long serialVersionUID = 1L; // // // delegation to core // // public void write(int c) throws IOException { core.write(c); } public void write(char[] cbuf) throws IOException { core.write(cbuf); } public void write(char[] cbuf, int off, int len) throws IOException { core.write(cbuf, off, len); } public void write(String str) throws IOException { core.write(str); } public void write(String str, int off, int len) throws IOException { core.write(str, off, len); } public Writer append(CharSequence csq) throws IOException { return core.append(csq); } public Writer append(CharSequence csq, int start, int end) throws IOException { return core.append(csq, start, end); } public Writer append(char c) throws IOException { return core.append(c); } public void flush() throws IOException { core.flush(); } public void close() throws IOException { core.close(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/SynchronousExecutorService.java0000644000175000017500000000300412122627370031411 0ustar jamespagejamespagepackage hudson.remoting; import java.util.Collections; import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; /** * {@link ExecutorService} that executes synchronously. * * @author Kohsuke Kawaguchi */ class SynchronousExecutorService extends AbstractExecutorService { private volatile boolean shutdown = false; private int count = 0; public void shutdown() { shutdown = true; } public List shutdownNow() { shutdown = true; return Collections.emptyList(); } public boolean isShutdown() { return shutdown; } public synchronized boolean isTerminated() { return shutdown && count==0; } public synchronized boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long end = System.currentTimeMillis() + unit.toMillis(timeout); while (count!=0) { long d = end - System.currentTimeMillis(); if (d<0) return false; wait(d); } return true; } public void execute(Runnable command) { if (shutdown) throw new IllegalStateException("Already shut down"); touchCount(1); try { command.run(); } finally { touchCount(-1); } } private synchronized void touchCount(int diff) { count += diff; if (count==0) notifyAll(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/GCCommand.java0000644000175000017500000000262012122627370025612 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; /** * Performs GC. * * @author Kohsuke Kawaguchi */ class GCCommand extends Command { protected void execute(Channel channel) { System.gc(); } private static final long serialVersionUID = 1L; } jenkins-remoting-2.23/src/main/java/hudson/remoting/ExportedClassLoaderTable.java0000644000175000017500000000430612122627370030704 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; /** * Manages unique ID for classloaders. * * @author Kohsuke Kawaguchi */ final class ExportedClassLoaderTable { private final Map> table = new HashMap>(); private final WeakHashMap reverse = new WeakHashMap(); // id==0 is reserved for bootstrap classloader private int iota = 1; public synchronized int intern(ClassLoader cl) { if(cl==null) return 0; // bootstrap classloader Integer id = reverse.get(cl); if(id==null) { id = iota++; table.put(id,new WeakReference(cl)); reverse.put(cl,id); } return id; } public synchronized ClassLoader get(int id) { WeakReference ref = table.get(id); if(ref==null) return null; return ref.get(); } } jenkins-remoting-2.23/src/main/java/hudson/remoting/InterceptingExecutorService.java0000644000175000017500000000562212122627370031522 0ustar jamespagejamespagepackage hudson.remoting; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; /** * {@link ExecutorService} that runs all the tasks in a given set of {@link CallableFilter}s. * @author Kohsuke Kawaguchi */ class InterceptingExecutorService extends DelegatingExecutorService { private final CopyOnWriteArrayList filters = new CopyOnWriteArrayList(); InterceptingExecutorService(ExecutorService base) { super(base); } public void addFilter(CallableFilter filter) { filters.add(filter); } public void removeFilter(CallableFilter filter) { filters.remove(filter); } @Override public void execute(Runnable command) { submit(command); } @Override public Future submit(Callable task) { return super.submit(wrap(task)); } @Override public Future submit(Runnable task) { return submit(task, null); } @Override public Future submit(Runnable task, T result) { return super.submit(wrap(task,result)); } @Override public List> invokeAll(Collection> tasks) throws InterruptedException { return super.invokeAll(wrap(tasks)); } @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { return super.invokeAll(wrap(tasks), timeout, unit); } @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { return super.invokeAny(wrap(tasks)); } @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return super.invokeAny(wrap(tasks), timeout, unit); } private Callable wrap(final Runnable r, final V value) { return wrap(new Callable() { public V call() throws Exception { r.run(); return value; } }); } private Collection> wrap(Collection> callables) { List> r = new ArrayList>(); for (Callable c : callables) { r.add(wrap(c)); } return r; } private Callable wrap(Callable r) { for (CallableFilter f : filters) r = applyFilter(r,f); return r; } private Callable applyFilter(final Callable inner, final CallableFilter filter) { return new Callable() { public V call() throws Exception { return filter.call(inner); } }; } } jenkins-remoting-2.23/src/main/java/hudson/remoting/PipeWriter.java0000644000175000017500000001465112122627370026123 0ustar jamespagejamespagepackage hudson.remoting; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; import java.util.concurrent.ExecutorService; /** * I/O task scheduler. * *

* Remoting handles I/O in a separate thread to prevent deadlock (JENKINS-5977), but that means occasionally * we need to synchronize between {@link Command}s and I/O (JENKINS-9189, JENKINS-7871, JENKINS-11251.) * * This class controls the task submission to I/O thread, and provides means to synchronize with it as follows: * *

    *
  1. * The sending {@link Channel} assigns an unique I/O ID (via {@link Channel#ioId} to every I/O {@link Command} * that it sends out. It also remembers the last I/O ID issued by the thread via {@link Channel#lastIoId}. * *
  2. * The receiving {@link Channel} uses that in {@link PipeWriter#submit(int, Runnable)} to enable re-discovery later. * *
  3. * {@link Future}s are maintained and made discoverable by their I/O ID as I/O operations take place. * *
  4. * When sending {@link Channel} later issues a {@link Request}, it uses {@link Channel#lastIoId} to recover * which I/O operation needs to take place before the {@link Request} can happen. * *
  5. * The receiving {@link Channel} honors that before it gets {@link Request}. * *
  6. * By the same token, the receiving {@link Channel} also records what I/O operations are issued by the * closure sent by {@link Request}. When it sends back {@link Response}, it also sends out the last I/O ID * issued by the closure ({@link Response#lastIoId}. * *
  7. * {@link Request} on the sending {@link Channel} honors this "last I/O ID" before it returns with the * response. *
* * I/O ID tracking and synchronization is per thread. This prevents the regression of JENKINS-5977. * *

Backward Compatibility

*

* When one side (say sender) predates I/O ID, the other side sees all I/O IDs as 0. So the receiver won't actually does the * {@link Future} book-keeping, and it will not wait for any I/O operations on its side, thanks to 0 being a special value. * Similarly, all the I/O ID tracking the receiver does get ignored by the sender. * *

Motivation

*

* This change is motivated by the fact that a certain degree of synchronization between {@link Request}/{@link Response}s * and I/O operations are desirable. The original issue was JENKINS-9189, which had the following: * *

 * OutputStream os = new RemoteOutputStream(...);
 * channel.call(new Callable() {
 *      os.write(...);
 * });
 * os.close();
 * 
* *

* The normal expectation is that by the time closure returns, all 'os.write()' operations are completed. * Yet since I/O and commands are separate, so unless the remoting library does synchronization, * 'os.close()' can happen before some of 'os.write()' calls, and ends up in a file truncation. * *

* That was fixed by 9cdd9cc0c5640beeb6bf36a4b26fa1ddcce7fd60 in the core originally, by adding * synchronization between I/O calls and {@link Response}, but then later * we discovered JENKINS-11251, which basically boils down to the following: * *

 * FilePath f = ...;
 * OutputStream os = f.write()
 * IOUtils.copy(data,os)
 * os.close()
 *
 * f.act(new Callable() {
 *     ... act on this newly created file ...
 * });
 * 
* *

* This now requires {@link Response} and I/O call coordination. * *

* This I/O ID based approach unifies both kind of coordination. * * @author Kohsuke Kawaguchi */ class PipeWriter { private static final class FutureHolder { private Future f; public synchronized Future set(Future f) { this.f = f; notifyAll(); return f; } public synchronized Future get() throws InterruptedException { while (f==null) wait(); return f; } } private final Map pendingIO = Collections.synchronizedMap(new HashMap()); /** * Actually carries out the {@link Runnable}s. */ private final ExecutorService base; public PipeWriter(ExecutorService base) { this.base = base; } public void shutdown() { base.shutdown(); } /** * @param id * I/O ID that used later in {@link #get(int)}. The value 0 has a special meaning * that indicates that no sync is needed later. Otherwise the caller is responsible * for assigning unique values. * * @return * Future object that can be used to wait for the completion of the submitted I/O task. */ public Future submit(final int id, final Runnable command) { if (id==0) return base.submit(command); // this indirection is needed to ensure that put happens before remove // if we store Future itself, then remove can happen before put, and // we'll end up leaking FutureHolder fh = new FutureHolder(); FutureHolder old = pendingIO.put(id, fh); assert old==null; return fh.set(base.submit(new Runnable() { public void run() { try { command.run(); } finally { FutureHolder old = pendingIO.remove(id); assert old!=null; } } })); } /** * Gets the {@link Future} object that can be used to wait for the completion of the submitted I/O task. * * Unlike POSIX wait() call, {@link PipeWriter} doesn't require that someone waits for the completion * of an I/O task. The consequence of that is that {@link PipeWriter} cannot differentiate between * invalid ID vs ID that was used and completed long time ago. In both cases, a {@link Future} object * that's already in the signaled state is returned. * * @return * never null. Unlike normal contract of {@link Future}, the object returned from this method * cannot necessarily be able to distinguish the normal and abnormal completion of the task. */ public Future get(int id) throws InterruptedException { FutureHolder f = pendingIO.get(id); if (f==null) return SIGNALED; // already completed return f.get(); } private static final Future SIGNALED = new AsyncFutureImpl(new Object()); } jenkins-remoting-2.23/src/main/resources/0000755000175000017500000000000012122627370021124 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/resources/hudson/0000755000175000017500000000000012122627370022424 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/resources/hudson/remoting/0000755000175000017500000000000012122627370024250 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/resources/hudson/remoting/jnlp/0000755000175000017500000000000012122627370025213 5ustar jamespagejamespagejenkins-remoting-2.23/src/main/resources/hudson/remoting/jnlp/title.png0000644000175000017500000003404212122627370027045 0ustar jamespagejamespagePNG  IHDR]d< CiCCPICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/9% pHYs  tIME$ IDATxսwHswjkfzvUݕ*tZ@ݳ$wk׮ cB`e!Zε=y^ggv1z͟/=zWnλm$qLQl>g>& i!R }j:aR ul/͢MM[kH)t~ζ/X, c& Er\n= C;m:vf6`v}.jhy~~bA$̰%aZrH)>RmNNO:r>T}BmZ\CK?Xk<><{&պHmAk!ֆ8Ic$!I,Bw?0Sc^ ΅tM|>vM>\h83Ǭ ZZ:{6NfFdi5dwˤE|`LLfs?:߼n\^&[l/-'# g~d2pֺYn.g 0.AgȽf C)%RIl6g8k-Ree7Wz껾& 'N/F|>LJcPJ!n4MzQ0C<߃=>IlJ+,өSox2EkbÇn?bv//}:M$I1F>{t!gG\Qo6Zc-Mw5/+w.юnH sp8FJ]? 0 6 Bpz c"xttz|tJKXk X1XgٶTFxGjz]ߓ)R;~< owZ- >;۞sp F5{z=19aISKǀ5Xc6,qוx/Ol??0-*Bl6 + Z ipyyIt6c2{~f<޽p_5<Li6#W@ |_~d XMoK"*VGy)x5֢$ISo&sl_~Jnm[Ž|_5A[hp8$)I?>FR.܈iaeDv18Io_ϣ՜Ç6ah[Ok>~dc ~ޓFv_~~G)T nW_ٗ^v_ |<>ޜ,{nnn~Nr`=Za?j6IӔ~`$ǷNMf7NP'6̐<-KqWnon nCQNC_X,e: ad'z%>S* Rq?V =R$xJfW 6ёǔH}S|%P8;9?sutpWy͏ylz·S2!=pBi58?9nk]JiU݋J(W3,(NjSLg !]{LZk:߿n3 q`k,^q>k(""D)#O})ajMXDluf9WyY8v%Na/?E bAEau 6V<<<Y x=v`Q!ܣ(xf {J'8Qs{.?S4`eR0DytnҫA@vl a Q)m fCn͚QKߘ{ŌG#t';::0=݉,6(W ) T3b6_0i6j{.F bnAHɚb{) GbN(ZRͮf!Nik4Z3NQRmbT+P 02/k͏[uZK<) 8N}o2f7^%Lz^iı1¥kX,"E9ҷ>IS]tKSͯB&Ph7U*襑.V\ǾB2[LRJ4UKjc{dtfJcՕez[$uD l#VM{j2Qzd%?FT(?@0CHKVr8e:]8E9k)*Ztpˉْ/fTKNZm拘1UoW3h)!6XKf,Nt1 ,m{ 2nk.Q_!2 k JI|.K{tMDs}TѮ/A<8řNM̰WwOZ{p/ۂ#o`hި#N:N2LVS kYPZ猠rb<)ψ >]kO/m2wp·G'G{V\~ހ{]|cXuVh\d:[B-yp( ƸR~_a2{eE,b!|N%)Is2*|^_m-]-5f#1?qcR ?\_JLY8l1v&w%,r#iSeV|x{qJ%[1IiB8lNMm`_T!Nxw$JI:{%:'3{nB;EyK蘻/w('s'C|NϜv?q{g'aa5EEL:J`mմ1HJY5:FQN#DH&eaQi__3-|vG- h5:>Qb:uƄ>}kp&0lVd~Z:`?<#^7F!~s`Th4$I$uDle]?/V2p58B,4I JV﹵45޽># OW,"G])gYV~5ߩݔ37]7s:O|zK2q܂Sj^"RUf( [UlR 6vTs)$痛 R| 7]/jbXΏ^I>=p4i@Qs%%{?xExNeԮ( W(2T/WmֆV?g}vEj!'GNW?MDkђ-c65ȸk!-M? !h5 S拈˳#Ϗ9:2p1)Sɡ^ˮ --+ý>2^renfOIn3Tx|b㻋a6ɚ@l[/K kknb2쨈j+'mj,PU-s/v_MLWF][/r@$kƇ45<=zrۍ7*UyAP\t'K\]5y "d%TfS-; {Y؜#!HEY1닓Og Ҍ-Gm0PvHFqqJ98ʣw2geLSXZ:kYO6UR%Le6t\.lԨNfU"B js10wko`4foFxhrvDŚhcVAV\qD$)nh K]ZSOߖ#}nQ3Mٜ>LJ{x: V{MjKo[ŮJ x5?}S霿秇 p4quD?$ ^]md-G22,[˲+ϖGeGcO=교Zl ZzIe!ESG]&"BJIZ*߈E^;==e>swwR|vSY,"8qh4q%lRZBo!{md֞J!{3R2Sh-RgO1n8  hZ;̍ˮ/wwx꺈f r!\\]5`o F !R/i4h5|v^EGf܏ٹj)$e:[0/MfL RFF#l6 `Z6J3^yCż҂ WgKl2[B."~QBXS&~CoPSRJ|Cf#; #?y~{2{u~NhBKmYlێ=<<â\ހ,[6u2[ҥYu5dpݬ_q}yIHSv+h^/~=?,Z̹e:s 18%NօT#w7{_Qua Jk9<3:prz#{M1w yDń].?oV4 |߿?C QgvW> \0θ3i55y*{Ћ]qb2)%ۇgR~|cDmMftVz|>W@L+.]3(OO=&9ф.F;Jx%SƓ}?yGQ'Iӝ9r F*ɇgvg9:WJr:f0 J'Lgs<ztt6' /l zﶗ}Vs1rV,1j?ƯfD/ͺu8kK]MJ)rcni7]P7$VvvRǧ>P Q"z(Ն6?`<|%dy[IhTOB467xSRpuH?BIUD׌Fj3iܔ'ݖ7ײԗ^Qb6ф~Ezڣ nu%L`L+T6۹{s}tTϿC fIDATL&jfUҦێvy1xĴ]Z|xo_/eBZ}fKYh[j^?]Idh*/[{ҟ. | RZ/ŻbMZQj]f2G>}{u^hݽv7.ф'FJfq2xrޤt j?GkX*&J4Mh >+ͫ3^]͌muIdZ\=B N3"*\ezlfnS|OCp LmҘhIBopyynN/%?=>qW3:B"<$tg'KhNRsɓeUɏ_/"굀=N |yj%b) 0N/"TA7xF&sѰ)G-xwg@8EWՑir5G_|)h|d7H?$OF g45$*6ʻ~&θf8bst8P*9T5UfIl73M^8.R kΥHˍJ矋Vk˫l(.8FZD (wDyuvۄO- ՀyhN(w-YFF?r!BMjT3_Dq \6YŴT3._`tJ4aUt;iw)Cc>G?vuUTF^h# 󭦊 I"#x <0 | pb͛m"l>r+d Ƙ,52IwI5OJQlP4>AXA FuW`5&*UUk6̦SJeY# 5],+HRcy||$IRb3X%6A gQawXLF![4jz$Z)ǞǑ~K]jvQ2WiR>I'OK;'Fqϗ/_0T*f̔tuƵJSlI"Ot4Ic WWLfs:ϾGCH",ScPkbpMo#U\DlX!m RH=Ʌ*3Fz r{A '"Bh6H7=3hβEm ave?"cѩ2,Yo"<soo .//uixu#qMHP)"O13{܃9?uC 62S>_)i{o<ž L6kSD֢AxS"<:XIbfsZQqww2` 7;EM̲+e~ƠQ̀{A BŌ~ϠgoCfll NVL,F➔tdn-wZӚH W߅K%k>1ق@})H8PqUѴ$hg8.%9+Jr< fs$$u)yIu(&x9cJGcb kn_aAIEVRӔ_`r[=Tű4)^M+|W*hNE̦S7Er RDH>B%̰&I}pj&pŀJRL;oެF n@y2ba0"ѡ.^]2+|FIJM,Xe |iXf#ТD[k$c Ό%$YKIb-GJ2Gmg'%M+ٓU/`C >d6Lnwox#q6)TPTT%(ܴ*sUĮkC`͊]덖$ziʳ9Y#OIqɊOQ7[(i!&TᚬNc ;Hw̌6Y&r e7[,w-+mW"*!&V 7wm] ckMS@M# ̿Ih3&g=*5J1:֊w-]W kK7ٗϻͥ6SE rbٔqYW S㌴^WYD O[(Zd 7iJ]21כ *eiEYt.V9d(?8Xv&hFJ>^< $.DfT\..%]R,jA՛RU[֖cbx2ՆXRtZ.2H{5q\SיN\pUSRppQ)q<*hcr05h4^2*t;&hBJV-ҵQlfEgڡ_;ޢG*ccHP} )@[7& C~Z^|WpbNvNI(r8Xb4XY %FB3^;&b1VV=a`a-!nD/|KRF.[C- i4lݻw$Iׯ_']כ0<3i樰$wv_S~Y@3n .ۤ Vn7!QT*r~W Ӯdjŝu4e?.BºI4W I\u7$vF <>=1ZkQ2xzq"sbdJjMk$dQcc t+;1bJ6ی^n~Ƕ(khd>52ц1<%)+*v,˸wĆ^eYu/aX͛\\^vv6jZZ-Fw2^rsl`s)oiJGJ2Bb iuLc_ݛ5D(1ӻmTi?eÏy~2wVLu6?^It2H%ovyfjUIENDB`jenkins-remoting-2.23/src/test/0000755000175000017500000000000012122627370017145 5ustar jamespagejamespagejenkins-remoting-2.23/src/test/java/0000755000175000017500000000000012122627370020066 5ustar jamespagejamespagejenkins-remoting-2.23/src/test/java/Driver8703.java0000644000175000017500000000327112122627370022511 0ustar jamespagejamespageimport hudson.remoting.PipeTest; import org.jvnet.hudson.test.Bug; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * Test bed to reproduce JENKINS-8703. * * @author Kohsuke Kawaguchi */ public class Driver8703 { @Bug(8703) public static void main(String[] args) throws Throwable { // int i=0; // while (true) { // System.out.println(i++); // foo(); // } ExecutorService es = Executors.newCachedThreadPool(); List flist = new ArrayList(); for (int i=0; i<10000; i++) { flist.add(es.submit(new Callable() { public Object call() throws Exception { Thread.currentThread().setName("testing"); try { foo(); return null; } catch (Exception e) { e.printStackTrace(); throw e; } catch (Throwable t) { t.printStackTrace(); throw new Exception(t); } finally { Thread.currentThread().setName("done"); } } })); } for (Future ff : flist) { ff.get(); } System.out.println("All done"); es.shutdown(); } private static void foo() throws Throwable { PipeTest t = new PipeTest(); t.setName("testSaturation"); t.runBare(); } } jenkins-remoting-2.23/src/test/java/TrafficAnalyzer.java0000644000175000017500000000045112122627370024015 0ustar jamespagejamespage/** * See {@link hudson.remoting.TrafficAnalyzer}. This entry point makes it easier to * invoke the tool. * * @author Kohsuke Kawaguchi */ public class TrafficAnalyzer { public static void main(String[] args) throws Exception { hudson.remoting.TrafficAnalyzer.main(args); } } jenkins-remoting-2.23/src/test/java/hudson/0000755000175000017500000000000012122627370021366 5ustar jamespagejamespagejenkins-remoting-2.23/src/test/java/hudson/remoting/0000755000175000017500000000000012122627370023212 5ustar jamespagejamespagejenkins-remoting-2.23/src/test/java/hudson/remoting/ChannelTest.java0000644000175000017500000001312512122627370026267 0ustar jamespagejamespagepackage hudson.remoting; import hudson.remoting.ChannelRunner.Fork; import hudson.remoting.ChannelRunner.InProcessCompatibilityMode; import org.jvnet.hudson.test.Bug; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.net.URL; import java.net.URLClassLoader; import java.util.concurrent.TimeUnit; /** * @author Kohsuke Kawaguchi */ public class ChannelTest extends RmiTestBase { public void testCapability() { assertTrue(channel.remoteCapability.supportsMultiClassLoaderRPC()); } @Bug(9050) public void testFailureInDeserialization() throws Exception { try { channel.call(new CallableImpl()); fail(); } catch (IOException e) { // e.printStackTrace(); assertEquals("foobar",e.getCause().getCause().getMessage()); assertTrue(e.getCause().getCause() instanceof ClassCastException); } } private static class CallableImpl implements Callable { public Object call() throws IOException { return null; } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { throw new ClassCastException("foobar"); } } /** * Objects exported during the request arg capturing is subject to caller auto-deallocation. */ @Bug(10424) public void testExportCallerDeallocation() throws Exception { for (int i=0; i<100; i++) { final GreeterImpl g = new GreeterImpl(); channel.call(new GreetingTask(g)); assertEquals(g.name,"Kohsuke"); assertTrue("in this scenario, auto-unexport by the caller should kick in.", !channel.exportedObjects.isExported(g)); } } /** * Objects exported outside the request context should be deallocated by the callee. */ @Bug(10424) public void testExportCalleeDeallocation() throws Exception { for (int j=0; j<10; j++) { final GreeterImpl g = new GreeterImpl(); channel.call(new GreetingTask(channel.export(Greeter.class,g))); assertEquals(g.name,"Kohsuke"); boolean isExported = channel.exportedObjects.isExported(g); if (!isExported) { // it is unlikely but possible that GC happens on remote node // and 'g' gets unexported before we get to execute the above line // if so, try again. if we kept failing after a number of retries, // then it's highly suspicious that the caller is doing the deallocation, // which is a bug. continue; } // now we verify that 'g' gets eventually unexported by remote. // to do so, we keep calling System.gc(). for (int i=0; i<30 && channel.exportedObjects.isExported(g); i++) { channel.call(new GCTask()); Thread.sleep(100); } assertTrue( "Object isn't getting unexported by remote", !channel.exportedObjects.isExported(g)); return; } fail("in this scenario, remote will unexport this"); } public void testGetSetProperty() throws Exception { channel.setProperty("foo","bar"); assertEquals("bar", channel.getProperty("foo")); assertEquals("bar",channel.waitForProperty("foo")); ChannelProperty typedProp = new ChannelProperty(Class.class,"a type-safe property"); channel.setProperty(typedProp, Void.class); assertEquals(Void.class, channel.getProperty(typedProp)); assertEquals(Void.class, channel.waitForProperty(typedProp)); } public void testWaitForRemoteProperty() throws Exception { Future f = channel.callAsync(new WaitForRemotePropertyCallable()); assertEquals("bar", channel.waitForRemoteProperty("foo")); f.get(1, TimeUnit.SECONDS); } private static class WaitForRemotePropertyCallable implements Callable { public Void call() throws Exception { Thread.sleep(500); Channel.current().setProperty("foo","bar"); return null; } } public interface Greeter { void greet(String name); } private static class GreeterImpl implements Greeter, Serializable { String name; public void greet(String name) { this.name = name; } private Object writeReplace() { return Channel.current().export(Greeter.class,this); } } private static class GreetingTask implements Callable { private final Greeter g; public GreetingTask(Greeter g) { this.g = g; } public Object call() throws IOException { g.greet("Kohsuke"); return null; } } private static class GCTask implements Callable { public Object call() throws IOException { System.gc(); return null; } } public void testClassLoaderHolder() throws Exception { URLClassLoader ucl = new URLClassLoader(new URL[0]); ClassLoaderHolder h = channel.call(new Echo(new ClassLoaderHolder(ucl))); assertSame(ucl,h.get()); } private static class Echo implements Callable { private final T t; Echo(T t) { this.t = t; } public T call() throws RuntimeException { return t; } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/DummyClassLoaderTest.java0000644000175000017500000000331712122627370030131 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import junit.framework.TestCase; /** * @author Kohsuke Kawaguchi */ public class DummyClassLoaderTest extends TestCase { public void testLoad() throws Throwable { DummyClassLoader cl = new DummyClassLoader(this.getClass().getClassLoader()); Callable c = (Callable) cl.newTestCallable(); System.out.println(c.call()); // make sure that the returned class is loaded from the dummy classloader assertTrue(((Object[])c.call())[0].toString().startsWith(DummyClassLoader.class.getName())); } } jenkins-remoting-2.23/src/test/java/hudson/remoting/ChannelFilterTest.java0000644000175000017500000000241112122627370027431 0ustar jamespagejamespagepackage hudson.remoting; import java.util.concurrent.Callable; /** * @author Kohsuke Kawaguchi */ public class ChannelFilterTest extends RmiTestBase { public void testFilter() throws Exception { channel.addLocalExecutionInterceptor(new CallableFilter() { public V call(Callable callable) throws Exception { Object old = STORE.get(); STORE.set("x"); try { return callable.call(); } finally { STORE.set(old); } } }); Callable t = new Callable() { public Object call() throws Exception { return STORE.get(); } }; final Callable c = channel.export(Callable.class, t); assertEquals("x", channel.call(new CallableCallable(c))); } private final ThreadLocal STORE = new ThreadLocal(); private static class CallableCallable implements hudson.remoting.Callable { private final Callable c; public CallableCallable(Callable c) { this.c = c; } public Object call() throws Exception { return c.call(); } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/RemoteInvocationHandlerTest.java0000644000175000017500000000201512122627370031476 0ustar jamespagejamespagepackage hudson.remoting; import java.io.Serializable; public class RemoteInvocationHandlerTest extends RmiTestBase { public void testMethodSelection() throws Exception { final Impl i = new Impl(); channel.call(new Task(i)); assertEquals("value", i.arg); } public interface Contract { void meth(String arg1); } private static class Impl implements Contract, Serializable { String arg; public void meth(String arg1, String arg2) { assert false : "should be ignored"; } public void meth(String arg1) { this.arg = arg1; } private Object writeReplace() { return Channel.current().export(Contract.class, this); } } private static class Task implements Callable { private final Contract c; Task(Contract c) { this.c = c; } public Void call() throws Error { c.meth("value"); return null; } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/TestCallable.java0000644000175000017500000000455712122627370026427 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.InputStream; import java.io.ByteArrayOutputStream; /** * {@link Callable} used to verify the classloader used. * *

* This class can be also used as an exception to test that behaviour. * * @author Kohsuke Kawaguchi * @see DummyClassLoader */ public class TestCallable extends Exception implements Callable { public Object call() throws Throwable { Object[] r = new Object[4]; // to verify that this class is indeed loaded by the remote classloader r[0] = getClass().getClassLoader().toString(); // to make sure that we can also load resources String resName = "TestCallable.class"; InputStream in = getClass().getResourceAsStream(resName); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[8192]; int len; while((len=in.read(buf))>0) baos.write(buf,0,len); in.close(); r[1] = baos.toByteArray(); // to make sure multiple resource look ups are cached. r[2] = getClass().getResource(resName); r[3] = getClass().getResource(resName); return r; } public static class Sub extends TestCallable {} } jenkins-remoting-2.23/src/test/java/hudson/remoting/WithRunner.java0000644000175000017500000000316112122627370026163 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2010, InfraDNA, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * Specify the channel runners for a test case. * @author Kohsuke Kawaguchi */ @Retention(RUNTIME) @Target(TYPE) @Documented @Inherited public @interface WithRunner { Class[] value(); } jenkins-remoting-2.23/src/test/java/hudson/remoting/DummyClassLoader.java0000644000175000017500000001037712122627370027275 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.IOException; import java.io.InputStream; import java.io.File; import java.io.OutputStream; import java.io.FileOutputStream; import java.net.URL; import org.apache.commons.io.IOUtils; /** * Used to load a dummy class hudson.remoting.test.TestCallable * out of nowhere, to test {@link RemoteClassLoader} by creating a class * that only exists on one side of the channel but not the other. * * @author Kohsuke Kawaguchi */ class DummyClassLoader extends ClassLoader { private final String logicalName; private final String physicalPath; private final String logicalPath; public DummyClassLoader(ClassLoader parent) { this(parent, false); } public DummyClassLoader(ClassLoader parent, boolean child) { super(parent); if (child) { logicalName = "hudson.rem0ting.TestCallable$Sub"; physicalPath = "hudson/remoting/TestCallable$Sub.class"; logicalPath = "hudson/rem0ting/TestCallable$Sub.class"; } else { logicalName = "hudson.rem0ting.TestCallable"; physicalPath = "hudson/remoting/TestCallable.class"; logicalPath = "hudson/rem0ting/TestCallable.class"; } } /** * Loads a class that looks like an exact clone of {@link TestCallable} under * a different class name. */ public Object newTestCallable() { try { return loadClass("hudson.rem0ting.TestCallable").newInstance(); } catch (InstantiationException e) { throw new Error(e); } catch (IllegalAccessException e) { throw new Error(e); } catch (ClassNotFoundException e) { throw new Error(e); } } protected Class findClass(String name) throws ClassNotFoundException { if(name.equals(logicalName)) { // rename a class try { byte[] bytes = loadTransformedClassImage(); return defineClass(name,bytes,0,bytes.length); } catch (IOException e) { throw new ClassNotFoundException("Bytecode manipulation failed",e); } } return super.findClass(name); } private byte[] loadTransformedClassImage() throws IOException { InputStream in = getResourceAsStream(physicalPath); String data = IOUtils.toString(in, "ISO-8859-1"); // Single-character substitutions will not change length fields in bytecode etc. String data2 = data.replaceAll("remoting(.)Test", "rem0ting$1Test"); return data2.getBytes("ISO-8859-1"); } protected URL findResource(String name) { if (name.equals(logicalPath)) { try { File f = File.createTempFile("rmiTest","class"); OutputStream os = new FileOutputStream(f); os.write(loadTransformedClassImage()); os.close(); f.deleteOnExit(); return f.toURI().toURL(); } catch (IOException e) { return null; } } return super.findResource(name); } } jenkins-remoting-2.23/src/test/java/hudson/remoting/HexDumpTest.java0000644000175000017500000000036212122627370026270 0ustar jamespagejamespagepackage hudson.remoting; import junit.framework.TestCase; /** * @author Kohsuke Kawaguchi */ public class HexDumpTest extends TestCase { public void test1() { assertEquals("0001ff",HexDump.toHex(new byte[]{0,1,-1})); } } jenkins-remoting-2.23/src/test/java/hudson/remoting/PipeTest.java0000644000175000017500000002216412122627370025617 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.ChannelRunner.InProcessCompatibilityMode; import junit.framework.Test; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullOutputStream; import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.For; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.OutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Arrays; import java.util.concurrent.ExecutionException; /** * Test {@link Pipe}. * * @author Kohsuke Kawaguchi */ public class PipeTest extends RmiTestBase implements Serializable { /** * Test the "remote-write local-read" pipe. */ public void testRemoteWrite() throws Exception { Pipe p = Pipe.createRemoteToLocal(); Future f = channel.callAsync(new WritingCallable(p)); read(p); int r = f.get(); System.out.println("result=" + r); assertEquals(5,r); } /** * Have the reader close the read end of the pipe while the writer is still writing. * The writer should pick up a failure. */ @Bug(8592) @For(Pipe.class) public void testReaderCloseWhileWriterIsStillWriting() throws Exception { final Pipe p = Pipe.createRemoteToLocal(); final Future f = channel.callAsync(new InfiniteWriter(p)); final InputStream in = p.getIn(); assertEquals(in.read(), 0); in.close(); try { f.get(); fail(); } catch (ExecutionException e) { // should have resulted in an IOException if (!(e.getCause() instanceof IOException)) { e.printStackTrace(); fail(); } } } /** * Just writes forever to the pipe */ private static class InfiniteWriter implements Callable { private final Pipe pipe; public InfiniteWriter(Pipe pipe) { this.pipe = pipe; } public Void call() throws Exception { while (true) { pipe.getOut().write(0); Thread.sleep(10); } } } private static class WritingCallable implements Callable { private final Pipe pipe; public WritingCallable(Pipe pipe) { this.pipe = pipe; } public Integer call() throws IOException { write(pipe); return 5; } } /** * Test the "local-write remote-read" pipe. */ public void testLocalWrite() throws Exception { Pipe p = Pipe.createLocalToRemote(); Future f = channel.callAsync(new ReadingCallable(p)); write(p); int r = f.get(); System.out.println("result=" + r); assertEquals(5,r); } public void testLocalWrite2() throws Exception { Pipe p = Pipe.createLocalToRemote(); Future f = channel.callAsync(new ReadingCallable(p)); Thread.sleep(2000); // wait for remote to connect to local. write(p); int r = f.get(); System.out.println("result=" + r); assertEquals(5,r); } public interface ISaturationTest { void ensureConnected() throws IOException; int readFirst() throws IOException; void readRest() throws IOException; } public void testSaturation() throws Exception { if (channelRunner instanceof InProcessCompatibilityMode) return; // can't do this test without the throttling support. final Pipe p = Pipe.createLocalToRemote(); Thread writer = new Thread() { final Thread mainThread = Thread.currentThread(); // this makes it easy to see the relationship between the thread pair in the debugger @Override public void run() { OutputStream os = p.getOut(); try { byte[] buf = new byte[Channel.PIPE_WINDOW_SIZE*2+1]; os.write(buf); } catch (IOException e) { e.printStackTrace(); } } }; // 1. wait until the receiver sees the first byte. at this point the pipe should be completely clogged // 2. make sure the writer thread is still alive, blocking // 3. read the rest ISaturationTest target = channel.call(new CreateSaturationTestProxy(p)); // make sure the pipe is connected target.ensureConnected(); channel.syncLocalIO(); // then let the writer commence writer.start(); // make sure that some data arrived to the receiver // at this point the pipe should be fully clogged assertEquals(0,target.readFirst()); // the writer should be still blocked Thread.sleep(1000); assertTrue(writer.isAlive()); target.readRest(); } private static class CreateSaturationTestProxy implements Callable { private final Pipe pipe; public CreateSaturationTestProxy(Pipe pipe) { this.pipe = pipe; } public ISaturationTest call() throws IOException { return Channel.current().export(ISaturationTest.class, new ISaturationTest() { private InputStream in; public void ensureConnected() throws IOException { in = pipe.getIn(); } public int readFirst() throws IOException { return in.read(); } public void readRest() throws IOException { new DataInputStream(in).readFully(new byte[Channel.PIPE_WINDOW_SIZE*2]); } }); } } private static class ReadingCallable implements Callable { private final Pipe pipe; public ReadingCallable(Pipe pipe) { this.pipe = pipe; } public Integer call() throws IOException { read(pipe); return 5; } } private static void write(Pipe pipe) throws IOException { OutputStream os = pipe.getOut(); byte[] buf = new byte[384]; for( int i=0; i<256; i++ ) { Arrays.fill(buf,(byte)i); os.write(buf,0,256); } os.close(); } private static void read(Pipe p) throws IOException { InputStream in = p.getIn(); for( int cnt=0; cnt<256*256; cnt++ ) assertEquals(cnt/256,in.read()); assertEquals(-1,in.read()); in.close(); } public void _testSendBigStuff() throws Exception { OutputStream f = channel.call(new DevNullSink()); for (int i=0; i<1024*1024; i++) f.write(new byte[8000]); f.close(); } /** * Writer end closes even before the remote computation kicks in. */ public void testQuickBurstWrite() throws Exception { final Pipe p = Pipe.createLocalToRemote(); Future f = channel.callAsync(new Callable() { public Integer call() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOUtils.copy(p.getIn(), baos); return baos.size(); } }); OutputStream os = p.getOut(); os.write(1); os.close(); // at this point the async executable kicks in. // TODO: introduce a lock to ensure the ordering. assertEquals(1,(int)f.get()); } private static class DevNullSink implements Callable { public OutputStream call() throws IOException { return new RemoteOutputStream(new NullOutputStream()); } } public static Test suite() throws Exception { return buildSuite(PipeTest.class); } private Object writeReplace() { return null; } } jenkins-remoting-2.23/src/test/java/hudson/remoting/NonSerializableExceptionTest.java0000644000175000017500000000471412122627370031663 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.net.SocketException; /** * @author Kohsuke Kawaguchi */ public class NonSerializableExceptionTest extends RmiTestBase { /** * Makes sure non-serializable exceptions are gracefully handled. * * HUDSON-1041. */ public void test1() throws Throwable { try { channel.call(new Failure()); } catch (ProxyException p) { // verify that we got the right kind of exception assertTrue(p.getMessage().contains("NoneSerializableException")); assertTrue(p.getMessage().contains("message1")); ProxyException nested = p.getCause(); assertTrue(nested.getMessage().contains("SocketException")); assertTrue(nested.getMessage().contains("message2")); assertNull(nested.getCause()); } } private static final class NoneSerializableException extends Exception { private final Object o = new Object(); // this is not serializable private NoneSerializableException(String msg, Throwable cause) { super(msg, cause); } } private static final class Failure implements Callable { public Object call() throws Throwable { throw new NoneSerializableException("message1",new SocketException("message2")); } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/PipeWriterTestChecker.java0000644000175000017500000000026312122627370030275 0ustar jamespagejamespagepackage hudson.remoting; /** * @author Kohsuke Kawaguchi */ public interface PipeWriterTestChecker { void assertSlowStreamNotTouched(); void assertSlowStreamTouched(); } jenkins-remoting-2.23/src/test/java/hudson/remoting/ChannelRunner.java0000644000175000017500000001442712122627370026627 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, InfraDNA, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.Channel.Mode; import junit.framework.Assert; import java.io.IOException; import java.io.File; import java.io.OutputStream; import java.io.FileOutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.net.URLClassLoader; import java.net.URL; import org.apache.commons.io.output.TeeOutputStream; import org.apache.commons.io.FileUtils; /** * Hides the logic of starting/stopping a channel for test. * * @author Kohsuke Kawaguchi */ interface ChannelRunner { Channel start() throws Exception; void stop(Channel channel) throws Exception; String getName(); /** * Runs a channel in the same JVM. */ static class InProcess implements ChannelRunner { private ExecutorService executor; /** * failure occurred in the other {@link Channel}. */ private Exception failure; public Channel start() throws Exception { final FastPipedInputStream in1 = new FastPipedInputStream(); final FastPipedOutputStream out1 = new FastPipedOutputStream(in1); final FastPipedInputStream in2 = new FastPipedInputStream(); final FastPipedOutputStream out2 = new FastPipedOutputStream(in2); executor = Executors.newCachedThreadPool(); Thread t = new Thread("south bridge runner") { public void run() { try { Channel s = new Channel("south", executor, Mode.BINARY, in2, out1, null, false, null, createCapability()); s.join(); System.out.println("south completed"); } catch (IOException e) { e.printStackTrace(); failure = e; } catch (InterruptedException e) { e.printStackTrace(); failure = e; } } }; t.start(); return new Channel("north", executor, Mode.BINARY, in1, out2, null, false, null, createCapability()); } public void stop(Channel channel) throws Exception { channel.close(); System.out.println("north completed"); executor.shutdown(); if(failure!=null) throw failure; // report a failure in the south side } public String getName() { return "local"; } protected Capability createCapability() { return new Capability(); } } static class InProcessCompatibilityMode extends InProcess { public String getName() { return "local-compatibility"; } @Override protected Capability createCapability() { return Capability.NONE; } } /** * Runs a channel in a separate JVM by launching a new JVM. */ static class Fork implements ChannelRunner { private Process proc; private ExecutorService executor; private Copier copier; public Channel start() throws Exception { System.out.println("forking a new process"); // proc = Runtime.getRuntime().exec("java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000 hudson.remoting.Launcher"); System.out.println(getClasspath()); proc = Runtime.getRuntime().exec(new String[]{"java","-cp",getClasspath(),"hudson.remoting.Launcher"}); copier = new Copier("copier",proc.getErrorStream(),System.out); copier.start(); executor = Executors.newCachedThreadPool(); OutputStream out = proc.getOutputStream(); if (RECORD_OUTPUT) { File f = File.createTempFile("remoting",".log"); System.out.println("Recording to "+f); out = new TeeOutputStream(out,new FileOutputStream(f)); } return new Channel("north", executor, proc.getInputStream(), out); } public void stop(Channel channel) throws Exception { channel.close(); channel.join(10*1000); // System.out.println("north completed"); executor.shutdown(); copier.join(); int r = proc.waitFor(); // System.out.println("south completed"); Assert.assertEquals("exit code should have been 0",0,r); } public String getName() { return "fork"; } public String getClasspath() { // this assumes we run in Maven StringBuilder buf = new StringBuilder(); URLClassLoader ucl = (URLClassLoader)getClass().getClassLoader(); for (URL url : ucl.getURLs()) { if (buf.length()>0) buf.append(File.pathSeparatorChar); buf.append(FileUtils.toFile(url)); // assume all of them are file URLs } return buf.toString(); } /** * Record the communication to the remote node. Used during debugging. */ private static boolean RECORD_OUTPUT = false; } } jenkins-remoting-2.23/src/test/java/hudson/remoting/DeadRemoteOutputStreamTest.java0000644000175000017500000000355512122627370031333 0ustar jamespagejamespagepackage hudson.remoting; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; /** * @author Kohsuke Kawaguchi */ public class DeadRemoteOutputStreamTest extends RmiTestBase implements Serializable { /** * If the remote writing end reports {@link IOException}, then the writing end shall * eventually see it. */ public void testDeadWriterNotification() throws Exception { final OutputStream os = new RemoteOutputStream(new OutputStream() { @Override public void write(int b) throws IOException { System.gc(); DummyClassLoader cl = new DummyClassLoader(this.getClass().getClassLoader()); throw (IOException)new IOException(MESSAGE).initCause((Exception) cl.newTestCallable()); } }); channel.call(new Callable() { public Void call() throws Exception { os.write(0); // this write will go through because we won't notice that it's dead System.gc(); Thread.sleep(1000); try { for (int i=0; i<100; i++) { os.write(0); System.gc(); Thread.sleep(10); } fail("Expected to see the failure"); } catch (IOException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String whole = sw.toString(); assertTrue(whole, whole.contains(MESSAGE) && whole.contains("hudson.rem0ting.TestCallable")); } return null; } }); } public static final String MESSAGE = "dead man walking"; } jenkins-remoting-2.23/src/test/java/hudson/remoting/CopyThread.java0000644000175000017500000000356012122627370026123 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; /** * Thread that copies a stream. * * @author Kohsuke Kawaguchi */ class Copier extends Thread { private final InputStream in; private final OutputStream out; public Copier(String threadName, InputStream in, OutputStream out) { super(threadName); this.in = in; this.out = out; } public void run() { try { byte[] buf = new byte[8192]; int len; while((len=in.read(buf))>0) out.write(buf,0,len); in.close(); } catch (IOException e) { // TODO: what to do? } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/TrafficAnalyzer.java0000644000175000017500000000314212122627370027141 0ustar jamespagejamespagepackage hudson.remoting; import hudson.remoting.RemoteInvocationHandler.RPCRequest; import java.io.File; import java.io.FileInputStream; import java.io.DataInputStream; import java.io.ObjectInputStream; /** * A little forensic analysis tool to figure out what information master and slaves are exchanging. * *

* Use the tee command or network packet capturing tool to capture the traffic between the master and * the slave, then run it through this tool to get the dump of what commands are sent between them. * * @author Kohsuke Kawaguchi */ public class TrafficAnalyzer { public static void main(String[] args) throws Exception { File f = new File("/home/kohsuke/ws/hudson/investigations/javafx-windows-hang/out.log"); DataInputStream fin = new DataInputStream(new FileInputStream(f)); fin.readFully(new byte[4]); // skip preamble ObjectInputStream ois = new ObjectInputStream(fin); for (int n=0; ; n++) { Command o = (Command)ois.readObject(); System.out.println("#"+n+" : "+o); if (o instanceof RPCRequest) { RPCRequest request = (RPCRequest) o; System.out.print(" ("); boolean first=true; for (Object argument : request.getArguments()) { if(first) first=false; else System.out.print(","); System.out.print(argument); } System.out.println(")"); } if (o.createdAt!=null) o.createdAt.printStackTrace(System.out); } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/ChannelTestSuite.java0000644000175000017500000000402112122627370027274 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import junit.framework.Test; import junit.framework.TestSuite; import java.util.Enumeration; /** * {@link TestSuite} that configures {@link RmiTestBase} as they are added. * *

* This allows the same test method to be run twice with different * {@link ChannelRunner}. * * @author Kohsuke Kawaguchi */ public class ChannelTestSuite extends TestSuite { public ChannelTestSuite(Class testClass, Class channelRunner) { super(testClass); // I can't do this in addTest because it happens in the above constructor! Enumeration en = tests(); while (en.hasMoreElements()) { Test test = (Test) en.nextElement(); if(test instanceof RmiTestBase && channelRunner!=null) { ((RmiTestBase)test).setChannelRunner(channelRunner); } } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/ClassRemotingTest.java0000644000175000017500000001261512122627370027474 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.ChannelRunner.InProcessCompatibilityMode; import junit.framework.Test; import org.objectweb.asm.ClassReader; import org.objectweb.asm.commons.EmptyVisitor; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.jvnet.hudson.test.Bug; /** * Test class image forwarding. * * @author Kohsuke Kawaguchi */ public class ClassRemotingTest extends RmiTestBase { private static final String CLASSNAME = "hudson.rem0ting.TestCallable"; public void test1() throws Throwable { // call a class that's only available on DummyClassLoader, so that on the remote channel // it will be fetched from this class loader and not from the system classloader. DummyClassLoader cl = new DummyClassLoader(this.getClass().getClassLoader()); Callable c = (Callable) cl.newTestCallable(); Object[] r = (Object[]) channel.call(c); System.out.println(r[0]); assertTrue(r[0].toString().startsWith("hudson.remoting.RemoteClassLoader@")); // make sure the bytes are what we are expecting System.out.println("Resource is "+((byte[])r[1]).length+" bytes"); ClassReader cr = new ClassReader((byte[])r[1]); cr.accept(new EmptyVisitor(),false); // make sure cache is taking effect System.out.println(r[2]); System.out.println(r[3]); assertEquals(r[2],r[3]); } /** * Tests the use of user-defined classes in remote property access */ public void testRemoteProperty() throws Exception { // this test cannot run in the compatibility mode without the multi-classloader serialization support, // because it uses the class loader specified during proxy construction. if (channelRunner instanceof InProcessCompatibilityMode) return; DummyClassLoader cl = new DummyClassLoader(this.getClass().getClassLoader()); Callable c = (Callable) cl.newTestCallable(); assertSame(c.getClass().getClassLoader(), cl); channel.setProperty("test",c); channel.call(new RemotePropertyVerifier()); } @Bug(6604) public void testRaceCondition() throws Throwable { DummyClassLoader parent = new DummyClassLoader(ClassRemotingTest.class.getClassLoader()); DummyClassLoader child1 = new DummyClassLoader(parent, true); final Callable c1 = (Callable) child1.loadClass(CLASSNAME + "$Sub").newInstance(); assertEquals(child1, c1.getClass().getClassLoader()); assertEquals(parent, c1.getClass().getSuperclass().getClassLoader()); DummyClassLoader child2 = new DummyClassLoader(parent, true); final Callable c2 = (Callable) child2.loadClass(CLASSNAME + "$Sub").newInstance(); assertEquals(child2, c2.getClass().getClassLoader()); assertEquals(parent, c2.getClass().getSuperclass().getClassLoader()); ExecutorService svc = Executors.newFixedThreadPool(2); RemoteClassLoader.TESTING = true; try { java.util.concurrent.Future f1 = svc.submit(new java.util.concurrent.Callable() { public Object call() throws Exception { return channel.call(c1); } }); java.util.concurrent.Future f2 = svc.submit(new java.util.concurrent.Callable() { public Object call() throws Exception { return channel.call(c2); } }); f1.get(); f2.get(); } finally { RemoteClassLoader.TESTING = false; } } public static Test suite() throws Exception { return buildSuite(ClassRemotingTest.class); } private static class RemotePropertyVerifier implements Callable { public Object call() throws IOException { Object o = Channel.current().getRemoteProperty("test"); assertEquals(o.getClass().getName(), CLASSNAME); assertTrue(Channel.class.getClassLoader() != o.getClass().getClassLoader()); assertTrue(o.getClass().getClassLoader() instanceof RemoteClassLoader); return null; } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/RmiTestBase.java0000644000175000017500000000560112122627370026241 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, InfraDNA, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import hudson.remoting.ChannelRunner.InProcess; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Base class for remoting tests. * * @author Kohsuke Kawaguchi */ @WithRunner({ ChannelRunner.InProcess.class, ChannelRunner.InProcessCompatibilityMode.class, ChannelRunner.Fork.class }) public abstract class RmiTestBase extends TestCase { protected transient Channel channel; protected transient ChannelRunner channelRunner = new InProcess(); protected void setUp() throws Exception { System.out.println("Starting "+getName()); channel = channelRunner.start(); } protected void tearDown() throws Exception { channelRunner.stop(channel); } /*package*/ void setChannelRunner(Class runner) { try { this.channelRunner = runner.newInstance(); } catch (InstantiationException e) { throw new Error(e); } catch (IllegalAccessException e) { throw new Error(e); } } public String getName() { return super.getName()+"-"+channelRunner.getName(); } /** * Can be used in the suite method of the derived class to build a * {@link TestSuite} to run the test with all the available * {@link ChannelRunner} configuration. */ protected static Test buildSuite(Class testClass) { TestSuite suite = new TestSuite(); WithRunner wr = testClass.getAnnotation(WithRunner.class); for( Class r : wr.value() ) { suite.addTest(new ChannelTestSuite(testClass,r)); } return suite; } } jenkins-remoting-2.23/src/test/java/hudson/remoting/throughput/0000755000175000017500000000000012122627370025423 5ustar jamespagejamespagejenkins-remoting-2.23/src/test/java/hudson/remoting/throughput/DumbSender.java0000644000175000017500000000165512122627370030325 0ustar jamespagejamespagepackage hudson.remoting.throughput; import org.apache.commons.io.IOUtils; import java.io.ByteArrayInputStream; import java.net.Socket; import java.util.Random; import java.util.concurrent.TimeUnit; /** * @author Kohsuke Kawaguchi */ public class DumbSender { public static void main(String[] args) throws Exception { byte[] payload = getRandomSequence(); for (int i=0; i<2; i++) { Socket s = new Socket("127.0.0.2",DumbReceiver.PORT); System.out.println("Started"); long start = System.nanoTime(); IOUtils.copy(new ByteArrayInputStream(payload), s.getOutputStream()); s.close(); System.out.println("Done: "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-start)); } } private static byte[] getRandomSequence() { byte[] buf = new byte[10*1024*1024]; new Random(0).nextBytes(buf); return buf; } } jenkins-remoting-2.23/src/test/java/hudson/remoting/throughput/Sender.java0000644000175000017500000000510512122627370027507 0ustar jamespagejamespagepackage hudson.remoting.throughput; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.Future; import hudson.remoting.Pipe; import hudson.remoting.SocketInputStream; import hudson.remoting.SocketOutputStream; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullOutputStream; import org.junit.Assert; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertArrayEquals; /** * @author Kohsuke Kawaguchi */ public class Sender { public static void main(String[] args) throws Exception { byte[] payload = getRandomSequence(); byte[] digest = digest(new ByteArrayInputStream(payload)); while (true) { Socket s = new Socket("127.0.0.2",Receiver.PORT); s.setTcpNoDelay(true); Channel ch = new Channel("bogus", Executors.newCachedThreadPool(), new BufferedInputStream(new SocketInputStream(s)), new BufferedOutputStream(new SocketOutputStream(s))); final Pipe p = Pipe.createLocalToRemote(); Future f = ch.callAsync(new Callable() { public byte[] call() throws Exception { return digest(p.getIn()); } }); System.out.println("Started"); long start = System.nanoTime(); IOUtils.copy(new ByteArrayInputStream(payload),p.getOut()); p.getOut().close(); f.get(); System.out.println("Done: "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-start)); assertArrayEquals(digest, f.get()); // verify the correctness of the result ch.close(); ch.join(); s.close(); } } private static byte[] digest(InputStream in) throws NoSuchAlgorithmException, IOException { DigestOutputStream dos = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("MD5")); IOUtils.copy(in, dos); return dos.getMessageDigest().digest(); } private static byte[] getRandomSequence() { byte[] buf = new byte[10*1024*1024]; new Random(0).nextBytes(buf); return buf; } } jenkins-remoting-2.23/src/test/java/hudson/remoting/throughput/DumbReceiver.java0000644000175000017500000000122212122627370030637 0ustar jamespagejamespagepackage hudson.remoting.throughput; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullOutputStream; import java.net.ServerSocket; import java.net.Socket; /** * @author Kohsuke Kawaguchi */ public class DumbReceiver { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(PORT); while (true) { System.out.println("Ready"); Socket s = ss.accept(); System.out.println("Accepted"); IOUtils.copy(s.getInputStream(), new NullOutputStream()); s.close(); } } public static final int PORT = 9533; } jenkins-remoting-2.23/src/test/java/hudson/remoting/throughput/Receiver.java0000644000175000017500000000202112122627370030025 0ustar jamespagejamespagepackage hudson.remoting.throughput; import hudson.remoting.Channel; import hudson.remoting.SocketInputStream; import hudson.remoting.SocketOutputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executors; /** * Accepts a channel one at a time. * * @author Kohsuke Kawaguchi */ public class Receiver { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(PORT); while (true) { System.out.println("Ready"); Socket s = ss.accept(); s.setTcpNoDelay(true); System.out.println("Accepted"); Channel ch = new Channel("bogus", Executors.newCachedThreadPool(), new BufferedInputStream(new SocketInputStream(s)), new BufferedOutputStream(new SocketOutputStream(s))); ch.join(); s.close(); } } public static final int PORT = 9532; }jenkins-remoting-2.23/src/test/java/hudson/remoting/PipeWriterTest.java0000644000175000017500000001337412122627370027017 0ustar jamespagejamespagepackage hudson.remoting; import java.io.IOException; import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.Serializable; /** * @author Kohsuke Kawaguchi */ public class PipeWriterTest extends RmiTestBase implements Serializable, PipeWriterTestChecker { /** * {@link OutputStream} that is slow to act. */ transient SlowOutputStream slow = new SlowOutputStream(); RemoteOutputStream ros = new RemoteOutputStream(slow); /** * Proxy that can be used from the other side to verify the state. */ PipeWriterTestChecker checker; @Override protected void setUp() throws Exception { super.setUp(); checker = channel.export(PipeWriterTestChecker.class, this, false); } /** * Base test case for the response / IO coordination. */ abstract class ResponseIoCoordCallable implements Callable { public Object call() throws Exception { long start = System.currentTimeMillis(); System.out.println("touch"); touch(); assertTrue(System.currentTimeMillis()-start<1000); // write() itself shouldn't block class CheckerThread extends Thread { Throwable death; @Override public void run() { try { checker.assertSlowStreamNotTouched(); } catch (Throwable t) { death = t; } } } // if we call back from another thread, we should be able to see // that the I/O operation hasn't completed. // note that if we run the code from the same thread, // that call back will synchronize against the earlier I/O. System.out.println("taking a look from another thread"); CheckerThread t = new CheckerThread(); t.start(); t.join(); if (t.death!=null) throw new AssertionError(t.death); System.out.println("returning"); return null; } /** * This is where the actual I/O happens. */ abstract void touch() throws IOException; } /** * Verifies that I/O that happens during closure execution and return of closure is * coordinated. */ public void testResponseIoCoord() throws Exception { channel.call(new ResponseIoCoordCallable() { void touch() throws IOException { ros.write(0); } }); // but I/O should be complete before the call returns. assertTrue(slow.written); } /** * Ditto for {@link OutputStream#flush()} */ public void testResponseIoCoordFlush() throws Exception { channel.call(new ResponseIoCoordCallable() { void touch() throws IOException { ros.flush(); } }); assertTrue(slow.flushed); } /** * Ditto for {@link OutputStream#close()} */ public void testResponseIoCoordClose() throws Exception { channel.call(new ResponseIoCoordCallable() { void touch() throws IOException { ros.close(); } }); assertTrue(slow.closed); } /** * Base test case for the request / IO coordination. */ abstract class RequestIoCoordCallable implements Callable { public Object call() throws IOException { long start = System.currentTimeMillis(); System.out.println("touch"); touch(); System.out.println("verify"); assertTrue(System.currentTimeMillis()-start<1000); // write() itself shouldn't block checker.assertSlowStreamTouched(); // but this call should System.out.println("end"); return null; } /** * This is where the actual I/O happens. */ abstract void touch() throws IOException; } public void testRequestIoCoord() throws Exception { channel.call(new RequestIoCoordCallable() { void touch() throws IOException { ros.write(0); } }); assertSlowStreamTouched(); } public void testRequestIoCoordFlush() throws Exception { channel.call(new RequestIoCoordCallable() { void touch() throws IOException { ros.flush(); } }); assertSlowStreamTouched(); } public void testRequestIoCoordClose() throws Exception { channel.call(new RequestIoCoordCallable() { void touch() throws IOException { ros.close(); } }); assertSlowStreamTouched(); } public void assertSlowStreamNotTouched() { assertFalse(slow.closed); assertFalse(slow.flushed); assertFalse(slow.written); } public void assertSlowStreamTouched() { assertTrue(slow.closed || slow.flushed || slow.written); } /** * Induces delay. */ class SlowOutputStream extends OutputStream { boolean closed,flushed,written; @Override public void write(int b) throws IOException { slow(); written = true; } @Override public void close() throws IOException { slow(); closed = true; } @Override public void flush() throws IOException { slow(); flushed = true; } private void slow() throws InterruptedIOException { try { Thread.sleep(3000); } catch (InterruptedException e) { throw new InterruptedIOException(); } } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/SimpleTest.java0000644000175000017500000001062512122627370026152 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import java.util.concurrent.CancellationException; import junit.framework.Test; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; /** * Testing the basic features. * * @author Kohsuke Kawaguchi */ public class SimpleTest extends RmiTestBase { public void test1() throws Exception { int r = channel.call(new Callable1()); System.out.println("result=" + r); assertEquals(5,r); } public void test1Async() throws Exception { Future r = channel.callAsync(new Callable1()); System.out.println("result="+r.get()); assertEquals(5,(int)r.get()); } private static class Callable1 implements Callable { public Integer call() throws RuntimeException { System.err.println("invoked"); return 5; } } public void test2() throws Exception { try { channel.call(new Callable2()); fail(); } catch (RuntimeException e) { assertEquals(e.getMessage(),"foo"); } } public void test2Async() throws Exception { try { Future r = channel.callAsync(new Callable2()); r.get(); fail(); } catch (ExecutionException e) { assertEquals(e.getCause().getMessage(),"foo"); } } private static class Callable2 implements Callable { public Integer call() throws RuntimeException { throw new RuntimeException("foo"); } } /** * Makes sure that proxied object can be sent back to the origin and resolve correctly. */ public void test3() throws Exception { Foo c = new Foo() {}; Foo r = channel.call(new Echo(channel.export(Foo.class,c))); assertSame(c,r); } public static interface Foo {} private static class Echo implements Callable { private final T t; Echo(T t) { this.t = t; } public T call() throws RuntimeException { return t; } } /** * Checks whether {@link Future#cancel} behaves according to spec. * Currently seems to be used by MavenBuilder.call and Proc.RemoteProc.kill * (in turn used by MercurialSCM.joinWithTimeout when polling on remote host). */ //@Bug(4611) public void testCancellation() throws Exception { Cancellable task = new Cancellable(); Future r = channel.callAsync(task); r.cancel(true); try { r.get(); fail("should not return normally"); } catch (CancellationException x) { // right } assertTrue(r.isCancelled()); assertFalse(task.ran); // XXX ought to also test various other aspects: cancelling before start, etc. } private static class Cancellable implements Callable { boolean ran; public Integer call() throws InterruptedException { Thread.sleep(9999); ran = true; return 0; } } public static Test suite() throws Exception { return buildSuite(SimpleTest.class); } } jenkins-remoting-2.23/src/test/java/hudson/remoting/PrefetchTest.java0000644000175000017500000000373212122627370026462 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import org.objectweb.asm.ClassReader; import org.objectweb.asm.attrs.StackMapAttribute; import java.io.IOException; /** * @author Kohsuke Kawaguchi */ public class PrefetchTest extends RmiTestBase { public void testPrefetch() throws Exception { VerifyTask vt = new VerifyTask(); assertTrue( channel.preloadJar(vt,ClassReader.class)); assertFalse(channel.preloadJar(vt,ClassReader.class)); // TODO: how can I do a meaningful test of this feature? System.out.println(channel.call(vt)); } private static class VerifyTask implements Callable { public String call() throws IOException { StackMapAttribute sma = new StackMapAttribute(); return Which.jarFile(sma.getClass()).getPath(); } } } jenkins-remoting-2.23/src/test/java/hudson/remoting/BinarySafeStreamTest.java0000644000175000017500000001643112122627370030121 0ustar jamespagejamespage/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.remoting; import junit.framework.TestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Random; /** * @author Kohsuke Kawaguchi */ public class BinarySafeStreamTest extends TestCase { public void test1() throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); OutputStream o = BinarySafeStream.wrap(buf); byte[] data = "Sending some data to make sure it's encoded".getBytes("UTF-8"); o.write(data); o.close(); InputStream in = BinarySafeStream.wrap(new ByteArrayInputStream(buf.toByteArray())); for (byte b : data) { int ch = in.read(); assertEquals(b, ch); } assertEquals(-1,in.read()); } public void testSingleWrite() throws IOException { byte[] ds = getDataSet(65536); String master = Base64.encode(ds); ByteArrayOutputStream buf = new ByteArrayOutputStream(); OutputStream o = BinarySafeStream.wrap(buf); o.write(ds,0,ds.length); o.close(); assertEquals(buf.toString(),master); } public void testChunkedWrites() throws IOException { byte[] ds = getDataSet(65536); String master = Base64.encode(ds); Random r = new Random(0); for( int i=0; i<16; i++) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); OutputStream o = BinarySafeStream.wrap(buf); randomCopy(r,new ByteArrayInputStream(ds),o,false); assertEquals(buf.toString(),master); } } public void testRoundtripNoFlush() throws IOException { _testRoundtrip(false); } public void testRoundtripFlush() throws IOException { _testRoundtrip(true); } private void _testRoundtrip(boolean flush) throws IOException { byte[] dataSet = getDataSet(65536); Random r = new Random(0); for(int i=0; i<16; i++) { if(dump) System.out.println("test started"); ByteArrayOutputStream buf = new ByteArrayOutputStream(); randomCopy(r,new ByteArrayInputStream(dataSet), BinarySafeStream.wrap(buf), flush); decodeByMaster(buf.toString(),dataSet); if(dump) System.out.println("------"); ByteArrayOutputStream dst = new ByteArrayOutputStream(); randomCopy(r,BinarySafeStream.wrap(new ByteArrayInputStream(buf.toByteArray())), dst,flush); byte[] result = dst.toByteArray(); if(!Arrays.equals(dataSet, result)) { String msg = print(result, 0, result.length); for( int j=0; j=ch && ch>=-1); // make sure the range is [-1,255] if(ch==-1) return; out.write(ch); break; case 1: int start = r.nextInt(16); int chunk = r.nextInt(16); int trail = r.nextInt(16); byte[] tmp = new byte[start+chunk+trail]; int len = in.read(tmp, start, chunk); if(dump) System.out.println("read2("+print(tmp,start,len)+",len="+len+",chunk="+chunk+")"); if(len==-1) return; // check extra data corruption for( int i=0; i