Java >> Java tutorial >  >> JDK

Om en helt speciel oplevelse:JDK6, JDK5 og Spring 2.0.0

I et af vores nuværende projekter migrerer vi fra JDK5 Update 7 til JDK6 Update 12. En af vores applikationer bruger JCaptcha til at sikre inputformularer. Vi opnår den størst mulige lethed ved at konfigurere captcha'en gennem foråret.

Efter at have skiftet en af ​​vores udviklingsmaskiner til JDK6, stod vi pludselig over for en IllegalArgumentException under initialisering af captcha-servleten. At skifte JBoss tilbage til JDK5 løste problemet. Dette førte dog ingen steder hen, fordi målsystemerne alle vil blive skiftet til Java 6.

Dette er et uddrag fra stak-sporet:

java.lang.IllegalArgumentException: Color parameter outside of expected range: Red Green Blue
	at java.awt.Color.testColorValueRange(Color.java:298)
	at java.awt.Color.(Color.java:382)
	at java.awt.Color.(Color.java:357)
	at java.awt.Color.(Color.java:448)

java.lang.IllegalArgumentException:Farveparameter uden for forventet område:Rød Grøn Blå ved java.awt.Color.testColorValueRange(Color.java:298) ved java.awt.Color.(Color.java:382) ved java.awt. Color.(Color.java:357) på java.awt.Color.(Color.java:448)

En mere dybdegående undersøgelse af problemet ved hjælp af Eclipse Debugger gav følgende resultat:

Det er klart, at farvekonstruktøren til tre flydende argumenter blev brugt. Et uddrag fra fjederbønner-konfigurationen:

<bean id="captchaBackgroundColor" class="java.awt.Color">
	<constructor-arg index="0"><value>255</value></constructor-arg>
	<constructor-arg index="1"><value>255</value></constructor-arg>
	<constructor-arg index="2"><value>255</value></constructor-arg>
</bean>

255 255 255

Float-konstruktøren af ​​java.awt.Color har dette som sin første kodelinje:

this( (int) (r*255+0.5), (int) (g*255+0.5), (int) (b*255+0.5));

this( (int) (r*255+0,5), (int) (g*255+0,5), (int) (b*255+0,5));

Dette sender argumenterne til int-konstruktøren, som i vores tilfælde kaldes Color(65025, 65025, 65025). Det umiddelbare resultat af dette er den førnævnte IllegalArgumentException.

Den nøjagtige årsag til vores problem stammer fra en kombination af flere fakta. Hvis du ikke er meget interesseret i tekniske detaljer, så spring følgende liste over:

  • I projektet er Spring 2.0.0 brugt. Konstruktøren, der skal kaldes, løses via ConstructorResolver.autowireConstructor(...). På linje 100 i den metode skabes en række potentielle konstruktører via refleksion. Dernæst sorteres dette array. Den underliggende Arrays.sort(…) giver et andet resultat med JDK6 end med JDK5 (dette ser ud til kun at ske på Windows-maskiner)
  • I det sorterede array ligger konstruktøren Color(float, float, float) på et lavere indeks end Color(int, int, int). Med JDK5 er det modsatte tilfældet.
  • Det følgende er en løkke, der vælger en konstruktør, der skal bruges til instansiering fra arrayet. Denne løkke bestemmer konstruktøren, der skal bruges baseret på en sammenligning af "antal argumenter" (trivielt) og en såkaldt "Type Difference Weight" (ikke så trivielt).
  • Typeforskelvægt betyder, at en forskel i klassehierarki beregnes for parametertyper og de relaterede argumenter. Vi ønsker at bruge int-argumenter for at instansiere vores farveobjekt. Metoden til beregning af typevægtforskel "bestiger" klassehierarkiet for hver parametertype, så længe der ikke kan findes en højere superklasse. Så længe den fundne superklasse er en type, som det relaterede argument kan tildeles, øges typeforskellens vægtværdi, og søgningen fortsætter. (AutowireUtils.getTypeDifferenceWeight(...))
  • Dette betyder naturligvis, at typeforskellens vægt af float og int er 0, da begge er primitiver.
  • Hvis den fundne tdw er mindre end den hidtil mindste tdw, bliver konstruktøren, der undersøges i den aktuelle sløjfekørsel, indstillet som den konstruktør, der skal bruges.
  • Fordi float-konstruktøren (ved hjælp af JDK6) er tættere på starten af ​​arrayet, og fordi tdw beregnet i yderligere loop-kørsler ikke kan blive mindre end den hidtil laveste tdw (0 kan ikke være mindre end 0), vil float-konstruktøren vil blive brugt til sidst.
  • Float-konstruktøren sender sine argumenter til int-konstruktøren, og den gør det ved at gange dem med 255 og øge dem med 0,5
  • Int-konstruktøren kaldes Color(65025, 65025, 65025). Dette resulterer i IllegalArgumentException, fordi RGB-værdier kun kan være mellem 0 og 255.

Tilbage til fænomenet:på testsystemet, der allerede er skiftet til Java 6, kører captchaen stadig uden fejl. Derfor er roden til problemet tydeligvis, at konstruktor-arrayet har en anden rækkefølge efter sortering på et Windows-system, end det har på et Linux-system. Typeforskellens vægtmekanisme skulle forhåbentlig også være blevet lidt smartere med nyere Spring-versioner, da autowiring trods alt stadig er afhængig af det.

Ikke desto mindre kan vi løse problemet ved at tilføje eksplicitte typeindstillinger til vores forårsbønner:

<bean id="captchaBackgroundColor" class="java.awt.Color">
	<constructor-arg index="0" type="int"><value>255</value></constructor-arg>
	<constructor-arg index="1" type="int"><value>255</value></constructor-arg>
	<constructor-arg index="2" type="int"><value>255</value></constructor-arg>
</bean>

255 255 255

Dette sikrer, at den ønskede konstruktør, som er java.awt.Color#Color(int r, int g, int b), bruges.

Kildekodeanalysen, jeg kørte i så meget dybde som muligt på Spring 2.0.0 og JDK6 Update 12, gav ikke en præcis konstatering af, hvorfor Arrays.sort(...) og den medfølgende komparator fra Spring frameworket giver et andet resultat på Windows systemer, end det gør på Linux-systemer. Enhver, der kan belyse dette spørgsmål, opfordres til at gøre det. 🙂

Konklusion:Djævelen er i detaljerne. Selv en antaget "triviel" ændring som en opdatering af Java-versionen kan føre til fejl, som er svære at finde. Intensive og præcise test er afgørende i sådanne tilfælde!

Mange tak til Mike Wiesner, Senior Consultant hos SpringSource, som hjalp mig "i realtid" med et spørgsmål vedrørende Spring 2.0.0!


Java tag