diff --git a/build.xml b/build.xml index 56f62562..3e014cc7 100755 --- a/build.xml +++ b/build.xml @@ -1,6 +1,8 @@ + + @@ -178,6 +180,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/erjang/EModuleLoader.java b/src/main/java/erjang/EModuleLoader.java index f8b854b0..19658c9b 100644 --- a/src/main/java/erjang/EModuleLoader.java +++ b/src/main/java/erjang/EModuleLoader.java @@ -24,6 +24,8 @@ import erjang.beam.EUtil; import erjang.beam.loader.ErjangBeamDisLoader; +import erjang.driver.efile.ClassPathResource; +import erjang.driver.efile.EFile; import erjang.util.Progress; @@ -112,6 +114,8 @@ private static File findBeamFile(String module) { File beam = new File(e, n + ".beam"); if (beam.exists()) return beam; + if (ClassPathResource.read_file(beam.getPath()) != null) + return beam; } return null; diff --git a/src/main/java/erjang/Main.java b/src/main/java/erjang/Main.java index 777180f6..7486b97d 100644 --- a/src/main/java/erjang/Main.java +++ b/src/main/java/erjang/Main.java @@ -22,6 +22,8 @@ import java.io.IOException; import java.util.ArrayList; +import erjang.driver.efile.EFile; + public class Main { public static final String SYSTEM_ARCHITECTURE = "java"; public static final String OTP_VERSION = ErjangConfig.getString("erjang.otp.version", "R13B04"); @@ -56,6 +58,10 @@ static String setup(String cmd_line_root) { private static String guess_erl_root() { + if (Main.class.getClassLoader().getResource("bin/start.boot") != null) { + return EFile.RESOURCE_PREFIX.substring(0, EFile.RESOURCE_PREFIX.length()-1); + } + // this logic works on Unixes ... what about windows? String path = System.getenv("PATH"); for (String elem : path.split(File.pathSeparator)) { @@ -154,7 +160,7 @@ public static void main(String[] args) throws Exception { System.setProperty("erjang.path", erl_bootstrap_ebindir); - if (!(new File(erl_bootstrap_ebindir)).exists()) { + if (!(new File(erl_bootstrap_ebindir)).exists() && !erl_bootstrap_ebindir.startsWith(EFile.RESOURCE_PREFIX)) { System.err.println("No bootstrap classes at: "+erl_bootstrap_ebindir); System.exit(1); } diff --git a/src/main/java/erjang/OTPMain.java b/src/main/java/erjang/OTPMain.java index 4784a7af..3898b155 100644 --- a/src/main/java/erjang/OTPMain.java +++ b/src/main/java/erjang/OTPMain.java @@ -18,6 +18,7 @@ package erjang; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -27,6 +28,8 @@ import java.util.logging.Logger; import erjang.driver.EDriver; +import erjang.driver.efile.ClassPathResource; +import erjang.driver.efile.EFile; /** * This will eventually be the main entrypoint for an OTP node. @@ -49,9 +52,24 @@ public class OTPMain { }; public static void load_modules_and_drivers() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { - for (String m : MODULES) { - ERT.load_module(EAtom.intern(m)); - } + + if (Main.erl_bootstrap_ebindir.startsWith(EFile.RESOURCE_PREFIX)) { + + for (String m : MODULES) { + String beam_path = Main.erl_bootstrap_ebindir + "/" + m + ".beam"; + EBinary bin = ClassPathResource.read_file(beam_path); + if (bin == null) { + throw new FileNotFoundException(beam_path); + } + EModuleLoader.load_module(m, bin); + } + + } else { + for (String m : MODULES) { + ERT.load_module(EAtom.intern(m)); + } + } + for (EDriver d : DRIVERS) { erjang.driver.Drivers.register(d); } diff --git a/src/main/java/erjang/driver/EDriverInstance.java b/src/main/java/erjang/driver/EDriverInstance.java index 8be97fb2..5daf2636 100644 --- a/src/main/java/erjang/driver/EDriverInstance.java +++ b/src/main/java/erjang/driver/EDriverInstance.java @@ -136,7 +136,7 @@ protected void driver_outputv(ByteBuffer hdr, ByteBuffer[] ev) throws Pausable { task.output_from_driver(res); } - protected void driver_output2(ByteBuffer header, ByteBuffer buf) throws Pausable { + public void driver_output2(ByteBuffer header, ByteBuffer buf) throws Pausable { int status = task.status; diff --git a/src/main/java/erjang/driver/IO.java b/src/main/java/erjang/driver/IO.java index c79a20c5..727e8660 100644 --- a/src/main/java/erjang/driver/IO.java +++ b/src/main/java/erjang/driver/IO.java @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.net.BindException; import java.net.ConnectException; @@ -29,6 +30,7 @@ import java.nio.charset.Charset; import java.util.zip.GZIPOutputStream; +import erjang.EBinary; import erjang.ERT; import erjang.driver.efile.Posix; @@ -218,4 +220,30 @@ public static String getstr(ByteBuffer buf, boolean term) { } } + static private class BARR2 extends ByteArrayOutputStream { + EBinary asBinary() { + return new EBinary(super.buf, 0, super.count); + } + } + + public static EBinary istream2binary(InputStream in) throws IOException { + BARR2 out = new BARR2(); + byte[] buf = new byte[4 * 1024]; + int read; + while ((read = in.read(buf)) > 0) { + out.write(buf, 0, read); + } + return out.asBinary(); + } + + public static byte[] istream2bytearray(InputStream in) throws IOException { + BARR2 out = new BARR2(); + byte[] buf = new byte[4 * 1024]; + int read; + while ((read = in.read(buf)) > 0) { + out.write(buf, 0, read); + } + return out.toByteArray(); + } + } diff --git a/src/main/java/erjang/driver/efile/ClassPathResource.java b/src/main/java/erjang/driver/efile/ClassPathResource.java new file mode 100644 index 00000000..2a112cb7 --- /dev/null +++ b/src/main/java/erjang/driver/efile/ClassPathResource.java @@ -0,0 +1,246 @@ +package erjang.driver.efile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Calendar; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import kilim.Pausable; + +import erjang.EBinList; +import erjang.EBinary; +import erjang.driver.IO; + +public class ClassPathResource { + + public static EBinary read_file(String name) { + + if (!name.startsWith(EFile.RESOURCE_PREFIX)) + return null; + + InputStream resource = ClassPathResource.class.getClassLoader() + .getResourceAsStream( + name.substring(EFile.RESOURCE_PREFIX.length())); + + if (resource == null) { + return null; + } else { + EBinary bin = null; + try { + bin = IO.istream2binary(resource); + return bin; + } catch (IOException e) { + return null; + } finally { + try { + resource.close(); + } catch (IOException e) { + } + } + } + + } + + public static void listdir(EFile eFile, String path) throws Pausable { + + String[] dir; + + try { + dir = list(path); + } catch (IOException e) { + eFile.reply_posix_error(IO.exception_to_posix_code(e)); + return; + } + + eFile.reply_list_directory(dir); + + } + + static String[] list(String path) throws IOException { + Enumeration out = ClassPathResource.class.getClassLoader() + .getResources(path); + + Set res = new HashSet(); + + while (out.hasMoreElements()) { + URL u = out.nextElement(); + list(res, u); + } + + return res.toArray(new String[res.size()]); + } + + static void list(Set res, URL url) throws IOException { + + if (url.getProtocol().equals("jar")) { + listJarURL(res, url); + } + if (url.getProtocol().equals("file")) { + File file = new File(url.getFile()); + for (String elm : file.list()) { + res.add(elm); + } + } else { + return; + } + + } + + private static void listJarURL(Set res, URL url) throws IOException { + String path = url.getPath(); + int bang = path.indexOf('!'); + String jar = path.substring("file:".length(), bang); + String elm = path.substring(bang + 2); + + ZipFile z = new ZipFile(jar); + Enumeration ents = z.entries(); + while (ents.hasMoreElements()) { + ZipEntry ent = ents.nextElement(); + if (ent.getName().startsWith(elm)) { + add(res, elm, ent.getName()); + } + } + + z.close(); + } + + private static void add(Set res, String elm, String name) { + String rest = name.substring(elm.length() + 1); + int idx; + if ((idx = rest.indexOf('/')) != -1) { + res.add(rest.substring(0, idx)); + } else if (rest.length() != 0) { + res.add(rest); + } + } + + public static void fstat(EFile efile, String file_name) throws Pausable { + + // System.err.println("trying entry for "+file_name); + + try { + ZipEntry ent; + + ent = get_entry(file_name + "/"); + if (ent == null) { + + ent = get_entry(file_name); + + if (ent == null) { + + efile.reply_posix_error(Posix.ENOENT); + return; + } + } + + // System.err.println("got entry for "+file_name+" : "+ent.toString()+" isdir="+ent.isDirectory()); + + long file_size = ent.getSize(); + int file_type = ent.isDirectory() ? EFile.FT_DIRECTORY + : EFile.FT_REGULAR; + + final int RESULT_SIZE = (1 + (29 * 4)); + + ByteBuffer res = ByteBuffer.allocate(RESULT_SIZE); + res.order(ByteOrder.BIG_ENDIAN); + + res.put(EFile.FILE_RESP_INFO); + res.putLong(file_size); + res.putInt(file_type); + + put_time(res, ent.getTime()); + put_time(res, ent.getTime()); + put_time(res, ent.getTime()); + + res.putInt(0000400); + res.putInt(1 /* file_links */); + res.putInt(0 /* file_major_device */); + res.putInt(0 /* file_minor_device */); + res.putInt(file_name.hashCode() /* file_inode */); + res.putInt(0 /* file_uid */); + res.putInt(0 /* file_gid */); + res.putInt(EFile.FA_READ); + + efile.driver_output2(res, null); + + } catch (IOException e) { + efile.reply_posix_error(IO.exception_to_posix_code(e)); + } + + } + + + private static void put_time(ByteBuffer res, long time) { + Calendar c = GregorianCalendar.getInstance(); + c.setTimeInMillis(time); + + int year = c.get(Calendar.YEAR); + res.putInt(year); + int month = c.get(Calendar.MONTH) - Calendar.JANUARY + 1; + res.putInt(month); + int day_of_month = c.get(Calendar.DAY_OF_MONTH); + res.putInt(day_of_month); + int hour_of_day = c.get(Calendar.HOUR_OF_DAY); + res.putInt(hour_of_day); + int minute_of_hour = c.get(Calendar.MINUTE); + res.putInt(minute_of_hour); + int seconds = c.get(Calendar.SECOND); + res.putInt(seconds); + } + + static ZipEntry get_entry(String path) throws IOException { + Enumeration out = ClassPathResource.class.getClassLoader() + .getResources(path.substring(EFile.RESOURCE_PREFIX.length())); + + while (out.hasMoreElements()) { + URL u = out.nextElement(); + ZipEntry result = get_entry(u); + if (result != null) + return result; + } + + return null; + } + + static ZipEntry get_entry(URL url) throws IOException { + + if (url.getProtocol().equals("jar")) { + return get_jar_entry(url); + } else { + return null; + } + + } + + private static ZipEntry get_jar_entry(URL url) throws IOException { + + // System.err.println("looking at "+url); + + String path = url.getPath(); + int bang = path.indexOf('!'); + String jar = path.substring("file:".length(), bang); + String elm = path.substring(bang + 2); + + ZipFile z = new ZipFile(jar); + + try { + ZipEntry ze = z.getEntry(elm); + if (ze != null) + return ze; + } finally { + z.close(); + } + + return null; + } + +} diff --git a/src/main/java/erjang/driver/efile/EFile.java b/src/main/java/erjang/driver/efile/EFile.java index fa7b526f..7e1b6c16 100644 --- a/src/main/java/erjang/driver/efile/EFile.java +++ b/src/main/java/erjang/driver/efile/EFile.java @@ -18,6 +18,7 @@ package erjang.driver.efile; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -25,6 +26,7 @@ import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -39,6 +41,7 @@ import java.util.concurrent.locks.Lock; import kilim.Pausable; +import erjang.EBinList; import erjang.EBinary; import erjang.EHandle; import erjang.EPort; @@ -56,6 +59,7 @@ */ public class EFile extends EDriverInstance { + public static final String RESOURCE_PREFIX = "/~resource/"; private static Field FileDescriptor_FD; static { @@ -775,6 +779,18 @@ public void ready() throws Pausable { reply_posix_error(Posix.ENOENT); return; } + + if (name.startsWith(RESOURCE_PREFIX)) { + + EBinary data = ClassPathResource.read_file(name); + if (data == null) { + reply_posix_error(Posix.ENOENT); + } else { + task.output_from_driver(new EBinList(FILE_RESP_OK_HEADER, data)); + } + + return; + } FileAsync d = new FileAsync() { private ByteBuffer binp = null; @@ -1475,6 +1491,11 @@ public void ready() throws Pausable { final String file_name = IO.strcpy(buf); final File file = ERT.newFile(file_name); + if (file_name.startsWith(RESOURCE_PREFIX)) { + ClassPathResource.fstat(this, file_name); + return; + } + d = new FileAsync() { long file_size; @@ -1576,6 +1597,12 @@ private void put_time(ByteBuffer res, long time) { case FILE_READDIR: { final String dir_name = IO.strcpy(buf); + + if (dir_name.startsWith(RESOURCE_PREFIX)) { + ClassPathResource.listdir(this, dir_name.substring(RESOURCE_PREFIX.length())); + return; + } + //final File cwd = new File(System.getProperty("user.dir")).getAbsoluteFile(); final File dir = ERT.newFile(/*cwd, */dir_name); @@ -1618,21 +1645,7 @@ public void ready() throws Pausable { return; } - for (int i = 0; i < files.length; i++) { - ByteBuffer resbuf = ByteBuffer.allocate(files[i].length()+1); - resbuf.put(FILE_RESP_OK); - resbuf.limit(resbuf.capacity()); - resbuf.position(1); - - IO.putstr(resbuf, files[i], false); - - driver_output2(resbuf, null); - } - - ByteBuffer resbuf = ByteBuffer.allocate(1); - resbuf.put(FILE_RESP_OK); - driver_output2(resbuf, null); - + reply_list_directory(files); } }; break; @@ -1882,5 +1895,22 @@ public String toString() { return "EFile[name=\""+name+"\";pos="+pos+"]"; } + + void reply_list_directory(String[] files) throws Pausable { + for (int i = 0; i < files.length; i++) { + ByteBuffer resbuf = ByteBuffer.allocate(files[i].length()+1); + resbuf.put(FILE_RESP_OK); + resbuf.limit(resbuf.capacity()); + resbuf.position(1); + + IO.putstr(resbuf, files[i], false); + + driver_output2(resbuf, null); + } + + ByteBuffer resbuf = ByteBuffer.allocate(1); + resbuf.put(FILE_RESP_OK); + driver_output2(resbuf, null); + } }