• Java: Dynamic class loading, part 2

    As we saw in a previous post, Java has the ability to dynamically load classes based on their name. We previously assumed that the classes being loaded had a constructor with no arguments. However, this restriction is ficticious. Let's now see how to load a class with random constructors.The first thing we have to do is to create a vector of Class objects that describes the constructors' prototype. Each element corresponds to a function parameter. Similarly, we later create a vector of Object instances that represents the parameters' values. For example, let's consider the following class:public class FooClass { public FooClass(String a, Integer b) { ... }}Having this, we'd do the following to prepare the call:Class[] proto = new Class[2];proto[0] = String.class;proto[1] = Integer.class;Object[] params = new Object[2];params[0] = "foo";params[1] = new Integer(5);Once we have this, we can proceed to create the instance of the desired class. To do this, we start by loading the class as we did in the former post. With the class loaded (as a Class object), we use the getConstructor method using the constructor's prototype (proto variable) as its single argument; this returns a Constructor object that represents the desired method.At last, we call the newInstance function over the Constructor object to create a new instance of the requested class (FooClass in our example). Shown as real code:Class c = Class.forName("FooClass");Constructor ct = c.getConstructor(proto);FooClass fc = (FooClass)ct.newInstance(params);Note that I've used the Integer class rather than the primitive int type; I'm not yet sure if you can dynamically call a function that uses primitive types in its signature. Also note that the class being loaded, as well as its constructor, are declared public; otherwise, the load will fail. [Continue reading]

  • RAID: Monograph

    I've just finished writing a monograph for the APC university course, a subject in which we study the PC architecture. My monograph talks about RAID and is 30 pages long. It starts describing what RAID is, which levels it has and ends up analyzing some current hardware and software solutions to set up a RAID system. Please note that it is in the Spanish language.Oh, by the way, this makes the 200 post! :-) [Continue reading]

  • NetBSD: File system directories, part 2

    In the first part, we saw what a directory is and gave some fuzzy ideas on how it is implemented. Let's now outline the most common operations run on directories: lookup and readdir.The lookup operation receives a path component name (a string without slashes) and returns the node pointed to by this name within the directory, assuming, of course, that the entry exists. Otherwise, it tells the caller that the entry is missing or incorrect (i.e., not a directory). This operation takes advantage of the name cache because it must be fast; keep in mind that lookups are executed extremely often.The implementation of the lookup operation, however, is very complex. It is cluttered by a weird locking protocol and has a lot of special cases. These include access advices, a technique used to tell the operation what kind of lookup is happening: a creation, a removal, a rename or a simple lookup. UFS uses these to locate empty holes in the directory while looking for an entry, among other things. tmpfs uses it to avoid two lookups for the same file on some operations, such as the creation.On the other hand, we have the readdir operation, the one used to read the contents of a directory. This operation is conceptually simple, as all it has to do is read as much entries as possible from the offset given to it. These entries are returned in a standard format, described in getdents(2).However, there is a tricky thing in readdir: the cookies. They are used by the NFS server to map directory entries to offsets within it so that further lookups can be done in a more efficient manner. For each entry returned by readdir, a cookie is also returned that specifies its physical offset inside the directory. A further call to this operation using the cookie's value could restart the read at the point where the entry lives.It is also interesting to note that some file systems return fake cookies because they do not have physical offsets within them — in other words, they are not stored on disk. This happens in, e.g., tmpfs or kernfs.Post suggested by Pavel Cahyna. [Continue reading]

  • How to prolong lithium-based batteries

    I've been recommeded to read the How to prolong lithium-based batteries article after asking how to manage my new iBook's battery. I'm posting the link here because I found it useful and interesing, so it can be helpful to you too. [Continue reading]

  • NetBSD: File system directories, part 1

    A file-system directory is an object that maps file names to nodes (i-nodes in UFS terminology). When given a file name, the directory must be able to tell whether it has that name or not and return the node number attached to it. File names are not stored in the nodes themselves as this allows for hard link creation flawlessly: you can have multiple directory entries pointing to the same file with no extra cost.Let's see a very simple directory implementation, coming from NetBSD's tmpfs (see tmpfs.h). This file system implements directories by using a linked list (struct tmpfs_dir) of directory entries (struct tmpfs_dirent). These structures look like:struct tmpfs_dirent { TAILQ_ENTRY(tmpfs_dirent) td_entries; uint16_t td_namelen; char *td_name; struct tmpfs_node *td_node;};TAILQ_HEAD(tmpfs_dir, tmpfs_dirent);Of special interest are the td_name field, which holds the entry name (file name), and the td_node pointer, which points to the file system node related to this directory entry (UFS could use i-node numbers instead).This implementation is really simple as it is completely backed by virtual memory; adding and removing entries is as easy as allocating a memory block and modifying the linked list accordingly. It could, of course, be more complex if it used a B+ tree or some other structure instead.However, on-disk file systems do extra tasks to optimize directory accesses. For example, when an entry is removed, it is marked as deleted using a special flag but it is not really removed from disk, because shrinking the directory could be expensive. Similarly, new entries overwrite previously deleted entries, if possible.In the next post, we will outline how some directory operations (such as lookup and readdir) work.Post suggested by Pavel Cahyna. [Continue reading]

  • Got new laptop: iBook 12'

    I never blogged about this before, but around six months ago, I bought a second-hand iBook 14", G3 at 600Mhz with 256Mb of RAM. The machine has served me very well during this time although I felt its slowliness (specially under Mac OS X) to do common tasks: buildling stuff (university projects) and composing documents under OpenOffice.org. I also felt its weight when carrying it on my backback.However, a friend of mine offered to buy it for a reasonable price some days ago... so I decided to sell it and buy a shiny new one. And this is what I've just done: gone to the closest Apple Store and bought an iBook 12" (G4 at 1.33Ghz, 1Gb of RAM, and 40Gb of hard disk). Less weight, faster and nicer :-)I'll keep using Mac OS X for a while in this computer. I want the laptop to do real work and not fight setting it up (as used to happen in the previous one with Debian GNU/Linux and NetBSD. Of course, I'll try them some day (or, at the very least, pkgsrc), but when I have more free time; I'm very busy these days with some university stuff (and I needed a faster laptop to get this work done).Now, to explore it a bit more! :-) [Continue reading]

  • Java: The finalize method

    I recently discovered that Java has finalization methods for objects, just like C++. However, they do not behave the same way due to Java's garbage collection.In Java, a finalization method has the following prototype: protected void finalize();, which is inherithed from java.lang.Object. This method can do any task it wants to explicitly release resources owned by an object.In C++ you have the following expectation: a finalization method is called whenever the object ceases to exist. That is: when a local object goes out of scope (all of them are deleted when the program shuts down) or when you explicitly delete a region of allocated memory. However, dynamically allocated objects are not automatically deleted when the program exits (you must remember to do it by hand).Java behaves much like C++'s dynamically allocated objects in this regard. If they are not "explicitly" deleted, their finalization method is not executed. And who deletes them? The GC when it feels it has to. This means that you cannot predict in any way when your object will be deleted; in fact, you cannot even know if the destroyer method will be ever executed. This is why Java classes provide methods to deallocate resources on purpose (see the close method on any class that affects files).Let's see a little example:class Test { protected void finalize() { System.out.println("Removed"); } public static void main(String[] args) { Test c = new Test(); }}If you build and run this program, it will probably print nothing. The GC doesn't act on shutdown so the object is implicitly removed when the VM closes. Instead, if you try the following, you will probably see something:class Test { protected void finalize() { System.out.println("Removed"); } public static void main(String[] args) { for (int i = 0; i Test c = new Test(); } }}It's a pity that destoryers do not work "correctly" (I mean, as in C++'s local variables), because this can make some pieces of code overly complex to control all possible execution flows.And sorry for the long delay in posting. I'm nowadays extremely busy with a university project and have few time for anything else. [Continue reading]