Java classpath

Kérdés: melyik jvm verziónál fog szöget ütni valaki fejében a gondolat, hogy a classpath paraméterében az egyik platformon pontosvesszővel, a másikon kettőspont az elválasztó karakter?

Linux: java -cp “.:lib/*”

Windows: -cp “.;lib/*”

Mondjuk így legalább mindenki gyorsan megtanulja, mit értenek a fejlesztők platformfüggetlen megoldások alatt.

Flutter 2 install

Nekem is úgy mondták (öregszem), de már egy napja megjelent a Flutter 2, ami mindennél is jobb. Mindig is szerettem a UI absztrakciókat, és azokat a durvábbnál durvább hackeket amibe aztán mindenki belebonyolódik az absztrakciók következményeként (SWT és társai ugye). Gondoltam felrakom, és jól megnézem, 2021-ben mit sikerült összehozni.

A telepítés (ami másolás) gond nélkül lement, aztán azt javasolták futtassam a flutter doctort, ami azt mondta, fogadjam el az android licenszeket egy újabb flutter doctor futtatással. Mégpedig ezzel:

flutter doctor --android-licenses

Lelkes voltam, futtattam és mint lenni szokott, megint egy szuper kis kaland vette kezdetét. Ugyanis a várt nyomj Y-t és fogadd el a licenszek helyett ezt kaptam:

C:\Users\voji\AppData\Local\Android\Sdk\tools\bin>flutter doctor --android-licenses
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
        at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156)
        at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75)
        at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81)
        at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
        at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:606)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 5 more

Nem igazán értettem a dolgot. Na jó értettem, ez a class ugyanis a jaxb része, ami már java 9 óta deprecated 11 óta meg benne sincs a JDK-ban. Elég sokan, elég sokat szívtak már ez miatt a nagyvilágban.

Szóval vagy átírogatom mindig a környezeti változókat, hogy java8-java15-java8 vagy keresek valami értelmesebb megoldást. Az utóbbit preferáltam, így legalább lehetőségem nyílt kicsit megismerkedni a dart nyelvvel is, ami állítólag flutterhez nem hátrány.

Szóval az történt, hogy flutter doki megpróbálja nyaggatni az android sdk parancssoros kezelőjét, az sdkmanager.bat-ot. Ha ezt önmagában lefuttatjuk, akkor ugyanezt a hibát fogjuk kapni, mert ott még valaki nem vette észre, hogy egy 2017 óta deprecated dolgot használ.

A megoldás innen már egyszerű: le kell tölteni egy jaxb-t, és megoldani, hogy a classpath-on legyen.

A letöltés viszonylag egyszreű, innen lehet megtenni. FYI a jobb oldali Download felirat nem egy menü, hanem funkció. Lehet a JDK fejlesztők se találták elsőre a letöltés gombot, azért maradt ki a jaxb a JDK-ból. Tanúság: tessék jól látható letöltés gombokat használni, lehetőleg az oldal közepén.

Na de visszatérve a problémára, azt az sdkmanager.bat generálta, célszerű itt is megoldani. Ez alapesetben itt található:

%USERPROFILE%\AppData\Local\Android\Sdk\tools\bin\sdkmanager.bat

A problémás rész pedig itt (ahol látható, hogy szépen felülírják a classpath-ot, pedig az első próbálkozásom az volt, hogy oda felteszem a dependenciákat mint környezeti változó):

set CLASSPATH=%APP_HOME%\lib\dvlib-26.0.0-dev.jar;%APP_HOME%\lib\jimfs-1.1.jar;%APP_HOME%...

Ha belematatjuk ezt a kis kiegészítést (ide lett kitömörítve a csomag: d:\work\libs\jaxb-ri):

set CLASSPATH=d:\work\libs\jaxb-ri\mod\jaxb-api.jar;d:\work\libs\jaxb-ri\mod\jaxb-runtime.jar;d:\work\libs\jaxb-ri\mod\istack-commons-runtime.jar;d:\work\libs\jaxb-ri\mod\javax.activation-api.jar;%APP_HOME%\lib\dvlib-26.0.0...

Máris boldogabb lesz egy flutter pingvin a google főhadiszállásán…

C:\Users\voji>flutter doctor --android-licenses
                                                                                
7 of 7 SDK package licenses not accepted. 100% Computing updates...
Review licenses that have not been accepted (y/N)?

Gradle parametrizált task

A gradle task-ok nem tudnak paramétereket fogadni by design. Globális build paramétereket lehet olvasni belőlük, de hát azért a gányolásnak is van határa…

És bár paramétert nem lehet task-nak adni, task-ot viszonylag egyszerűen lehet létrehozni. És igény esetén kicsit bonyolultabban is.

Tegyük fel angular-t szeretnénk fordítani, különböző build target-ekkel. Vagy másolunk sokat, vagy csinálunk egy ilyet:

def createAngularTask(build, language) {
    return tasks.create("buildAngular_${build}_${language}", Exec) {
        group = "generated"
        workingDir = project.ANGULAR_APP_PATH
        inputs.dir(Paths.get(workingDir.toString(), "/src"))
        outputs.dir(Paths.get(workingDir.toString(), "/dist_hu"))
        def cmdArray = ["npm", "run", "${build}:${language}"]
        if (System.getProperty('os.name').toUpperCase().contains('WINDOWS')) {
            cmdArray.addAll(0, ["cmd", "/c"])
        }
        commandLine cmdArray
    }
}

Kicsit emlékeztet ez a C-s define varázslásokra, de úgy tűnik a groovy-s csapatot ez nem rettentette el. Szóval ha csináltunk egy ilyet, akkor már csinálhatunk ilyesmiket is:

createAngularTask("aembuild", "hu")
createAngularTask("aembuild", "en")
createAngularTask("jarbuild", "hu")
createAngularTask("jarbuild", "en")

És ha ilyeneket csináltunk, akkor már csinálhatunk csoportos build task-okat copy paste nélkül:

task buildAngularAem {
    group = BasePlugin.BUILD_GROUP
    dependsOn('buildAngular_aembuild_hu')
    dependsOn('buildAngular_aembuild_en')
}

A fenti játékot el lehet játszani akár string tömbökből is, ha nagyon sok task lenne. Így a task-oknak, csak a képzeletünk szab határt…

JDK on Ubuntu

A Java telepítés nem egy nagy varázslat. Rendszerint arról szól, hogy kitömörítjük valahova a fájlokat, és elérjük, hogy amikor beírjuk hogy java, akkor az általunk kitömörített cucc induljon el.

Ez windows alatt egy download, total commander, és a PATH környezeti változó módosításával elérhető.

Ubuntu alatt főként csak terminált használok, szóval beírtam a google-be, hogy jdk install ubuntu, és vadabbnál vadabb dolgokat láttam, és nagyjából semmi se működött. Szóval ha valaki jdk-t szeretne telepíteni ubuntura, akkor az alábbit kell tenni:

Ellátogatni az OpenJdk oldalára, és kimásolni a nekünk tetsző JDK csomag URL-jét

Letölteni a kívánt csomagot:

wget https://download.java.net/java/GA/jdk15.0.1/51f4f36ad4ef43e39d0dfdbaf6549e32/9/GPL/openjdk-15.0.1_linux-x64_bin.tar.gz

Ki kell tömöríteni a cuccot amit letöltöttünk:

sudo tar -xvf openjdk-15.0.1_linux-x64_bin.tar.gz -C /usr/lib/jvm/

Fel kell venni az újonnan létrehozott JDK-a az alternatives közé:

sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-15.0.1/bin/java 1151

Utána pedig beállíthatjuk a használni kívánt java verziót az alábbi paranccsal:

sudo update-alternatives --config java

jar fájl futtatása windows alól

Egy ideig működött, majd nem… A megoldás:

Open up an administrator command window (this is needed if you’re using Vista or Windows 7 with UAC enabled) and do:

assoc .jar=jarfileterm
ftype jarfileterm=”C:\Program Files\Java\jre7\bin\java.exe” -jar “%1” %*

In your case, you should replace the C:\Program Files\Java\jre7\bin\java.exe path with the one for your install of the jre.

Forrás: http://stackoverflow.com/questions/10446986/double-clicking-jar-file-does-not-open-command-prompt

Java, jpeg, exception

Az egyik publikus képfeltöltő szervlet kapcsán jeleztek egy hibát, hogy valamiféle csodaképeket egyszerűen nem lehet feltölteni. A naplófájlok elemzése után látszott is a hiba:

java.lang.IllegalArgumentException: Numbers of source Raster bands and source color space components do not match
 at java.awt.image.ColorConvertOp.filter(ColorConvertOp.java:460)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.acceptPixels(JPEGImageReader.java:1114)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImage(Native Method)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1082)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:897)
 at javax.imageio.ImageIO.read(ImageIO.java:1422)
 at javax.imageio.ImageIO.read(ImageIO.java:1282)

A hiba viszonylag könnyen reprodukálható volt, tehát volt egy jó képem, és egy rossz. A gimp semmi extrát nem mondott a képről, és a rossz kép az ismételt elmentés után is rossz maradt.

Kis keresgélés után rátaláltam az exiftool nevű alkalmazásra:

exiftool -a -G ./JUT430-1.jpg

Látszott is, hogy a kép adatai között mindenféle furcsaság található:

[JFIF] JFIF Version : 1.01
 [JFIF] Resolution Unit : inches
 [JFIF] X Resolution : 300
 [JFIF] Y Resolution : 300
 [ICC_Profile] Profile CMM Type : HDM
 [ICC_Profile] Profile Version : 2.4.0
 [ICC_Profile] Profile Class : Output Device Profile
 [ICC_Profile] Color Space Data : CMYK
 [ICC_Profile] Profile Connection Space : Lab
 ...
[ICC_Profile] Profile Description : ISO Coated v2 (ECI)
 [ICC_Profile] Char Target : (Binary data 126677 bytes, use -b option to extract)
 [EXIF] Modify Date : 2012:08:21 09:56:35
 [EXIF] Compression : JPEG (old-style)
 [EXIF] Thumbnail Offset : 1829717
 [EXIF] Thumbnail Length : 5947

Ekkor gyanús lett a dolog, az én képeimen nem volt ennyi szemét. Letöröltem az összes exif adatot:

exiftool -all= ./JUT430-1.jpg

A dolog működött, a kép megnyitható java-ban. Innen már azt hittem egyszerű a dolog, már csak meg kellene oldani, hogy az exif adatokat a java is le tudja kapni feldolgozás előtt.

Erre találtam is egy ígéretes projektet az Apache common’s Sanselan-t. Az mondjuk furcsa volt, hogy még incubator-os, béta, és 3 éve senki nem nyúlt hozzá, de abból indultam ki, hogy biztosan azért mert annyira jó… 🙂

Természetesen a dolog nem működött. A forráskód tanulmányozása során feltűnt, hogy az általam használt kódból mintha hiányzott volna az érdemi rész…

</pre>
public void removeExifMetadata(ByteSource byteSource, OutputStream os)
 throws ImageReadException, IOException, ImageWriteException
 {
 JFIFPieces jfifPieces = analyzeJFIF(byteSource);
 List pieces = jfifPieces.pieces;

//Debug.debug("pieces", pieces);

//pieces.removeAll(jfifPieces.exifPieces);

//Debug.debug("pieces", pieces);

writeSegmentsReplacingExif(os, pieces, null);
 }
<pre>

Gondoltam ez így nem a legjobb, ezért visszacommenteztem (nyílt forráskód rulz) a removeAll-t. Sajnos ez még önmagában nem jelentett megoldást, ugyanis nem minden addicionális adat EXIF adat, és pont azok az adatok maradtak a képen, amik a problémát okozták.

Minden JFIFPieceSegment eltávolítása nem minősült kimondottan jó taktikának, de egy kis wikipedia-s utánaolvasás után megvolt, hogy mik az egyéb adatok marker bit-jei, amikre nekem semmi szükségem, innen már csak pár sor választott el a sikertől…


public void removeExifAndAllMetadata(ByteSource byteSource, OutputStream os)
 throws ImageReadException, IOException, ImageWriteException
 {
 JFIFPieces jfifPieces = analyzeJFIF(byteSource);
 List pieces = jfifPieces.pieces;

//Debug.debug("pieces", pieces);

 Iterator it = pieces.iterator();

 while (it.hasNext()) {
   Object object = (Object) it.next();
   if (object instanceof JFIFPieceSegment ) {
     JFIFPieceSegment iffdat=(JFIFPieceSegment)object;
     if (iffdat.marker>= 0xFFE0 && iffdat.marker<=0xFFEF) {
        it.remove();
     }
   }
 }

 pieces.removeAll(jfifPieces.exifPieces);

//Debug.debug("pieces", pieces);

writeSegmentsReplacingExif(os, pieces, null);
 }

A végeredmény valami ilyesmi lett:

public static void main(String[] args) {
 try {
String fileName="c:/JUT430-1.jpg";
File f=new File(fileName);
ExifRewriterAdv eRewriter=new ExifRewriterAdv();
FileInputStream fis=new FileInputStream(f);
ByteSourceInputStream bis=new ByteSourceInputStream(fis, null);
ByteArrayOutputStream bout=new ByteArrayOutputStream ();
eRewriter.removeExifAndAllMetadata(bis, bout);
ByteArrayInputStream iobis=new ByteArrayInputStream(bout.toByteArray());
ImageIO.read(iobis);
 } catch (Exception e) {
e.printStackTrace();
 }
 }

SyncToCloud.net

Nagy nehezen elindult végre a synctocloud.net.
Mint minden jó release esetén az utolsó pillanatokban még hibákat javítottam (egész pontosabban próbáltam megcsinálni azokat a dolgokat, amikről már azt hittem, hogy rég megcsináltam őket).
És mint minden jó program esetében itt sem az a kérdés, hogy van e benne hiba, inkább csak az, hogy mennyi 🙂

Eclipse Class cache

Az új eclipse verzió óta elég gyakran megesik velem, hogy egy két projektben egyszerűen nem működik az auto-import, open by type, és ehhez hasonló nyalánkságok.

Erre a megoldás egyszerű, törölni kell a workspace/.metadata/.plugins/org.eclipse.jdt.core könyvtárból a *.index and savedIndexNames.txt fájlokat…