001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.jni;
019
020import java.io.File;
021import java.io.FileOutputStream;
022import java.io.IOException;
023import java.util.List;
024import org.photonvision.common.hardware.Platform;
025import org.photonvision.common.logging.LogGroup;
026import org.photonvision.common.logging.Logger;
027
028public abstract class PhotonJNICommon {
029    public abstract boolean isLoaded();
030
031    public abstract void setLoaded(boolean state);
032
033    protected static Logger logger = null;
034
035    protected static synchronized void forceLoad(
036            PhotonJNICommon instance, Class<?> clazz, List<String> libraries) throws IOException {
037        if (instance.isLoaded()) return;
038        if (logger == null) logger = new Logger(clazz, LogGroup.Camera);
039
040        for (var libraryName : libraries) {
041            try {
042                // We always extract the shared object (we could hash each so, but that's a lot of work)
043                var arch_name = Platform.getNativeLibraryFolderName();
044                var nativeLibName = System.mapLibraryName(libraryName);
045                var in = clazz.getResourceAsStream("/nativelibraries/" + arch_name + "/" + nativeLibName);
046
047                if (in == null) {
048                    instance.setLoaded(false);
049                    return;
050                }
051
052                // It's important that we don't mangle the names of these files on Windows at least
053                File temp = new File(System.getProperty("java.io.tmpdir"), nativeLibName);
054                FileOutputStream fos = new FileOutputStream(temp);
055
056                int read = -1;
057                byte[] buffer = new byte[1024];
058                while ((read = in.read(buffer)) != -1) {
059                    fos.write(buffer, 0, read);
060                }
061                fos.close();
062                in.close();
063
064                System.load(temp.getAbsolutePath());
065
066                logger.info("Successfully loaded shared object " + temp.getName());
067
068            } catch (UnsatisfiedLinkError e) {
069                logger.error("Couldn't load shared object " + libraryName, e);
070                e.printStackTrace();
071                // logger.error(System.getProperty("java.library.path"));
072                instance.setLoaded(false);
073                return;
074            }
075        }
076        instance.setLoaded(true);
077    }
078
079    protected static synchronized void forceLoad(
080            PhotonJNICommon instance, Class<?> clazz, String libraryName) throws IOException {
081        forceLoad(instance, clazz, List.of(libraryName));
082    }
083}