001package com.github.sarxos.webcam.ds.civil;
002
003import java.io.File;
004import java.io.FileNotFoundException;
005import java.io.FileOutputStream;
006import java.io.IOException;
007import java.io.InputStream;
008import java.io.OutputStream;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013
014public class LtiCivilLoader {
015
016        private static final Logger LOG = LoggerFactory.getLogger(LtiCivilLoader.class);
017
018        /**
019         * Will be called until JVM shutdown.
020         * 
021         * @author Bartosz Firyn (SarXos)
022         */
023        private static class Deleter extends Thread {
024
025                private File file = null;
026
027                public Deleter(File file) {
028                        super("lti-civil-binary-deleter");
029                        this.file = file;
030                }
031
032                @Override
033                public void run() {
034                        super.run();
035                        if (file.exists()) {
036                                if (!file.delete()) {
037                                        LOG.warn(String.format("JVM was not able to remove file %s", file));
038                                }
039                        }
040                }
041        }
042
043        private LtiCivilLoader() {
044                // singleton
045        }
046
047        /**
048         * Copy bytes from a large (over 2GB) InputStream to an OutputStream.
049         * 
050         * @param input the InputStream to read from
051         * @param output the OutputStream to write to
052         * @return the number of bytes copied
053         * @throws NullPointerException if the input or output is null
054         * @throws IOException if an I/O error occurs
055         */
056        private static long copy(InputStream input, OutputStream output) throws IOException {
057                byte[] buffer = new byte[1024 * 4];
058                long count = 0;
059                int n = 0;
060                while (-1 != (n = input.read(buffer))) {
061                        output.write(buffer, 0, n);
062                        count += n;
063                }
064                return count;
065        }
066
067        protected static void load(String lib) {
068                LOG.info("Loading native library: " + lib);
069                try {
070                        System.loadLibrary(lib);
071                        LOG.info("DLL has been loaded from memory: " + lib);
072                } catch (UnsatisfiedLinkError e) {
073                        try {
074                                load("civil-" + System.currentTimeMillis(), lib);
075                        } catch (Exception e2) {
076                                LOG.error("Exception when loading DLL library", e2);
077                                throw new RuntimeException(e2);
078                        }
079                }
080        }
081
082        protected static void load(String path, String name) {
083
084                String libroot = "/META-INF/lib";
085                String libpath = null;
086                String libfile = null;
087
088                boolean arch64 = System.getProperty("os.arch").indexOf("64") != -1;
089                boolean linux = System.getProperty("os.name").toLowerCase().indexOf("linux") != -1;
090
091                if (linux) {
092                        libpath = libroot + (arch64 ? "/linux64/" : "/linux32/");
093                        libfile = "lib" + name + ".so";
094                } else {
095                        libpath = libroot + (arch64 ? "/win64/" : "/win32/");
096                        libfile = name + ".dll";
097                }
098
099                File parent = new File(System.getProperty("java.io.tmpdir") + "/" + path);
100                if (!parent.exists()) {
101                        if (!parent.mkdirs()) {
102                                throw new RuntimeException("Cannot create directory: " + parent.getAbsolutePath());
103                        }
104                }
105
106                File file = new File(parent, libfile);
107                if (!file.exists()) {
108
109                        boolean created = false;
110                        try {
111                                created = file.createNewFile();
112                        } catch (IOException e) {
113                                throw new RuntimeException("Not able to create file: " + file, e);
114                        }
115                        if (!created) {
116                                throw new RuntimeException("File cannot be created: " + file);
117                        }
118
119                        Runtime.getRuntime().addShutdownHook(new Deleter(file));
120                }
121
122                String resource = libpath + libfile;
123
124                LOG.debug("Library resource in JAR is {}", resource);
125
126                InputStream in = LtiCivilDriver.class.getResourceAsStream(resource);
127                if (in == null) {
128                        throw new RuntimeException("Resource not found: " + resource);
129                }
130
131                FileOutputStream fos = null;
132
133                try {
134                        fos = new FileOutputStream(file);
135                        copy(in, fos);
136                } catch (FileNotFoundException e) {
137                        throw new RuntimeException("File not found " + file, e);
138                } catch (IOException e) {
139                        throw new RuntimeException("IO exception", e);
140                } finally {
141                        if (in != null) {
142                                try {
143                                        in.close();
144                                } catch (IOException e) {
145                                        throw new RuntimeException("Cannot close input stream", e);
146                                }
147                        }
148                        if (fos != null) {
149                                try {
150                                        fos.close();
151                                } catch (IOException e) {
152                                        throw new RuntimeException("Cannot close file output stream", e);
153                                }
154                        }
155                }
156
157                LOG.debug("Loading library from file {}", file);
158
159                try {
160                        System.load(file.getAbsolutePath());
161                } catch (UnsatisfiedLinkError e) {
162                        throw new RuntimeException("Library file cannot be loaded: " + file, e);
163                }
164        }
165}