Last revised: 14May2003 GLGThis document describes all the concrete FileForker implementations provided in the MacBinary Toolkit 2 for Java. All necessary JARs and JNI-libraries are provided in ready-to-use form. You need not compile anything in order to use any of these concrete FileForker imps on any supported platform.
The source for all imps is provided, though some imps may not be recompileable on some platforms unless you obtain additional classes to compile against. The C source for the JNI-based imps may be somewhat difficult to recompile on Mac OS X unless you create a ProjectBuilder project for it. Details of recompilation are described in Rebuilding the Toolkit.
Table of Contents
If you haven't already read the overview, you should.
Before January 2003, no FileForker imp resolved alias-files on-the-fly. That is, no FileForker would automatically resolve alias-files that appeared as non-leaf directory-names leading up to some leaf element. The Mac OS X version would automatically resolve non-leaf symlinks on-the-fly, but Finder-style alias-files would not, and were definitely second-class citizens.In hindsight, this was stupid. At the very least, a FileForker imp should have been free to choose whether it would resolve non-leaf aliases on-the-fly or not. And that's exactly what I've now decided is the best approach:
Any FileForker implementation may resolve non-leaf aliases of any kind on-the-fly, so that aliases can appear as leading directories in Pathnames.Unfortunately, this is a new behavior, so none of the historical FileForker imps do so. Fortunately, a new alias-resolving imp is provided for every historical imp that didn't. Unfortunately, this means that the historical imps wired into MacPlatform are still non-resolving. Fortunately, this isn't a serious problem, because you can override what MacPlatform does (refer to its API docs), or use your own approach for designating a FileForker imp.New FileForker imps can either resolve-on-the-fly or not, and must always clearly state how they behave with non-leaf aliases in pathnames. For example, consider the new JNI-based FileForker imp for Mac OS X: MacOSXForker. Because on-the-fly resolving is generally more useful, I decided that this implementation would always resolve-on-the-fly. That means there isn't a non-resolving JNI-based FileForker, so if you need that behavior for some reason, you'll have to write it yourself.
On-the-fly alias-resolving never affects leaf aliases. Those are always identifiable as aliases, and must be explicitly resolved if that's what you want to happen. Otherwise, the leaf-alias itself is the item acted upon. Nor does resolve-on-the-fly affect the Pathname used by a FileForker. A FileForker's Pathname stays the same until you change it, or the FileForker is selfResolve()'ed.
Resolve-on-the-fly basically means that Finder-style alias-files are on roughly the same footing as Unix symlinks, and that's pretty much all it means. It's so simple, so powerful, and so obvious. That's why the initial design choice was so stupid.
These generic implementations are platform-neutral plain Java, and use java.io.File to refer to the file-system. They work on Mac OS, too, though there is a platform-aware selection of a default PathnameFormat, and their behavior is limited to whatever can be expressed with a java.io.File object.Typically, a java.io.File won't distinguish symlinks from original referents, so these classes effectively behave as resolve-on-the-fly imps. But only if that's how File behaves. If File doesn't resolve shortcuts or aliases, then these imps are NOT resolve-on-the-fly imps. Since File's behavior regarding shortcuts, symlinks, aliases, etc. is platform-dependent and largely undiscoverable, that's how these generic imps are constrained to act.
A java.io.File typically won't identify or distinguish leaf-aliases, either. For example, you can't distinguish a symlink to a directory from the original referent directory. Both kinds of File instances will return T from isDirectory(). Or maybe not, depending on platform.
All this indeterminacy may confuse some applications that use FileForker. This can't be helped, since it's all determined by how java.io.File behaves (or misbehaves).
- glguerin.io.imp.gen.PlainForker
The PlainForker class is a plain-Java implementation that simply acts as if resource-forks don't exist -- all resource-forks are always zero-length. This imp works on all platforms, but doesn't provide resource-forks on any of them. Its FileInfo and FileAccess capabilities are limited to what a java.io.File can provide.All non-Mac platforms default to the PlainForker implementation. It's what MacPlatform uses as the FileForker imp-of-last-resort when choosing one.
- glguerin.io.imp.gen.JavaOnlyForker
The JavaOnlyForker class is a PlainForker subclass that throws IOExceptions when asked to operate on resource-forks, but silently ignores other requests it can't complete. This imp is useful when you don't want to ignore resource-forks entirely, but you don't really support them, either.- glguerin.io.imp.gen.GenericForker
The GenericForker class is a PlainForker subclass that also acts as if resource-forks don't exist, but uses the cross-platform MRJToolkit (JAR supplied) for manipulating file-type and file-creator when possible. It's convenient when you want a generalized imp that supports a bare minimum of Mac OS capabilities.This imp will not work in Cocoa-Java apps on Mac OS X. The MRJToolkit facilities are implemented using JDirect, and JDirect is incompatible with Cocoa-Java apps. Cocoa-Java apps are compatible with PlainForker and JavaOnlyForker imps. Cocoa-Java apps are also compatible with the full-featured MacOSXForker imp described below.
Usage Considerations
These imps are present in the "All-MB.jar" file. You don't need to add anything to use most of them.The glguerin.io.imp.gen.PlainForker imp is the implementation of last resort built into glguerin.util.MacPlatform. If the methods selectFactoryName() or selectFactoryBinding() find no other suitable FileForker imp, then the PlainForker will be returned.
The glguerin.io.imp.gen.GenericForker imp needs "MRJToolkitStubs.jar" when it is run on non-Mac platforms. If those classes aren't present in your program's jar-file, or are not found in its classpath, then GenericForker will not work.
These implementations are for various versions of Java and Mac OS prior to Mac OS X.There are resolving and non-resolving versions of each imp. The non-resolving versions are the ones MacPlatform knows about. Since classical Mac OS didn't have much of a command-line, the non-resolving versions usually work fine. Only if you allow users to type pathnames, which may contain alias-files, would you absolutely need a resolve-on-the-fly imp. For flexibility, you might want to use a resolve-on-the-fly imp, simply so you don't have to worry about it.
- glguerin.io.imp.mac.jd1.JD1Forker
glguerin.io.imp.mac.jd1.ResolvingJD1Forker
These are the non-resolving and resolve-on-the-fly imps for classic Mac OS using JDirect-1. Both classes work under MRJ 2.0, which is the last version of MRJ to run on a 68040.Tested under MRJ 2.* on PowerPC 604e, and MRJ 2.0 on 68040. Recompiling this implementation with deprecation warnings enabled will result in warnings. That's because JDirect-1 has been deprecated by Apple for some time. However, it is the only JDirect mechanism available for older versions of MRJ, so this implementation has no other alternative but to endure the deprecations.
- glguerin.io.imp.mac.jd2.JD2Forker
glguerin.io.imp.mac.jd2.ResolvingJD2Forker
These are the non-resolving and resolve-on-the-fly imps for classical Mac OS using JDirect-2. Both classes work under MRJ 2.1 or 2.2.*.Tested under MRJ 2.1 and 2.2.* on a PowerPC 604e and a dual-G4, with various OSes from 7.6.1 through 9.2.1. Though tested on Mac OS 9, this implementation is not the default for that configuration. NineForker is the Mac OS 9 default.
- glguerin.io.imp.mac.jd2.NineForker
glguerin.io.imp.mac.jd2.ResolvingNineForker
These are the non-resolving and resolve-on-the-fly imps for Mac OS 9, implemented using JDirect 2. The implementation uses the FSRef-based API available only on Mac OS 9, so these classes will not work on any version of Mac OS before 9. Both classes will work in the Classic environment under Mac OS X.Both imps always resolve symlinks on-the-fly when running under Classic, but only ResolvingNineForker resolves Finder-aliases on-the-fly (under Classic or not). Both imps always provide long UniCode filenames, fork sizes over 2 GB, GMT-based time-stamps on HFS+ volumes, and Unix-style access privileges using FileAccess. The access privileges may be read and written under Mac OS 9, but they have no meaning to the OS itself. Privileges do have meaning under Classic, where they are honored.
Tested under MRJ 2.2.* on a dual-G4 with Mac OS X and Classic 9.0.4 or 9.2.1.
Usage Considerations
These imps are all present in the "All-MB.jar" file. You don't need to add anything to use them. Your program must be running on classical Mac OS for any of them to work.The non-resolving imps are assigned as the default imps built into glguerin.util.MacPlatform. One of these imps may be returned if the methods selectFactoryName() or selectFactoryBinding() decide they are a suitable FileForker imp for the current host platform.
The imps are arranged so that NineForker is used, if possible, followed in turn by an attempt to use JD2Forker and then JD1Forker.
If you want to automatically use the resolving imps, you will have to clear and then refill the Implicator.Chain returned by MacPlatform.getFactoryImplicators(). You can make the necessary Implicator instances using the MacPlatform.Imp nested class. Refer to the MacPlatform API docs, and the source, for more information.
These implementations are for Mac OS X. All imps always resolve symlinks on-the-fly. Only some imps resolve Finder-aliases on-the-fly.There are resolving and non-resolving versions of the default TenForker imp. MacPlatform only knows the non-resolving version.
The JDirect-based imps need no external native-code library. They are self-contained in the class-files or JAR alone. The JNI-based imps have their native methods implemented in a separate JNI library file. You must place this file in the same place as the JAR, or the Java classes won't work.
There isn't really a significant speed difference between the JDirect and JNI imps. Speed is dominated by I/O time, so any difference between JDirect and JNI overhead is insignificant. The JNI imps have noticeably shorter app-startup times, but only because JDirect adds to an app's startup time. This is a characteristic of JDirect, not of the FileForker imp itself.
The JDirect-based imps will not work in Cocoa-Java apps. JDirect technology is hostile to Cocoa event-loops and a fatal deadlock or wedge ensues. The JNI-based imps work in Cocoa-Java apps, and have been tested there. They should also work in background-only (faceless daemon) processes, which is another area that JDirect has problems with.
The JNI-based imps are the most capable and full-featured Mac OS X imps. I chose JNI for these imps because that's the most widely usable approach (see above), and because it's the future of Java on Mac OS X. As of this date, Apple has eliminated JDirect from Java 1.4.1 on Mac OS X. As a result, JNI is the only available technology for native-code implementations moving forward.
- glguerin.io.imp.mac.ten.TenForker
glguerin.io.imp.mac.ten.ResolvingTenForker
These imps work on Mac OS X 10.0 or higher. They are the non-resolving and resolve-on-the-fly imps for Mac OS X, implemented using JDirect-3. Both imps always resolve symlinks on-the-fly, but only ResolvingTenForker resolves Finder-aliases on-the-fly.Both imps provide long UniCode filenames, fork sizes over 2 GB, GMT-based time-stamps on HFS+ volumes, and Unix-style access privileges using FileAccess.
Both imps provide for creation of symlinks, but neither one can create Finder-alias files. Both support signalChange() to notify the Finder of changes to folders. No Watcher implementation is provided.
Neither implementation works in Cocoa-Java, nor under Java 1.4.1. For those environments, use the MacOSXForker imp, which is implemented using JNI.
- glguerin.io.imp.mac.macosx.MacOSXForker
This imp is for Mac OS X 10.1 or higher. It works in plain-Java or Cocoa-Java apps. It is implemented using JNI, so there is a supplementary native-code library-file that must be in place for it to work (place the lib-file with the jar-file).This imp always resolves symlinks and Finder-aliases on-the-fly. There isn't any non-resolving implementation. This imp also provides long UniCode filenames, fork sizes over 2 GB, GMT-based time-stamps on HFS+ volumes, and Unix-style access privileges using FileAccess.
This imp provides for creation of both symlinks and Finder-alias files. Finder-alias files are created with custom icons whenever possible, so they can be moved yet still be identifiable. This imp supports signalChange() to notify the Finder of changes to files and folders. It does not provide a Watcher implementation.
- glguerin.io.imp.mac.macosx.CarbonMacOSXForker
This imp is a subclass of MacOSXForker. It adds support for Watcher.This imp only works in plain-Java apps. It does not work in Cocoa-Java apps, because the Carbon event-loop needed to support the Watcher is hostile to the Cocoa-Java event-loop.
Other than providing a Watcher and requiring a Carbon event-loop, this imp has the same capabilities as its superclass, MacOSXForker.
Usage Considerations for JDirect Imps
The JDirect-based imps in the glguerin.io.imp.mac.ten package are present in the "All-MB.jar" file. Your program must be running on Mac OS X and Java 1.3.* for them to work. You don't need to add anything to use them.The glguerin.io.imp.mac.ten.TenForker imp is assigned as one of two possible default imps for Mac OS X built into glguerin.util.MacPlatform. The TenForker imp is returned as the default on Mac OS X 10.0, and on other versions of Mac OS X when MacOSXForker is unavailable. One of these imps may be returned if the methods selectFactoryName() or selectFactoryBinding() decide they are a suitable FileForker imp for the current host platform.
There are additional considerations for using the TenForker imp in a signed applet.
Usage Considerations for JNI Imps
The JNI-based imps in the glguerin.io.imp.mac.macosx package are located in the "MacOSXForker.jar" file, and supplemented by the "libMacOSXForkerIO.jnilib" file. Your program must be running on Mac OS X 10.1+ under Java 1.3.* or 1.4.* for them to work. YOU MUST TAKE EXTRA STEPS TO USE THESE IMPS.The glguerin.io.imp.mac.macosx.MacOSXForker imp is assigned as one of two possible default imps for Mac OS X built into glguerin.util.MacPlatform. The MacOSXForker imp is preferred over TenForker on 10.1 and higher, even though MacOSXForker is not supplied in "All-MB.jar". This was done because MacOSXForker generally has more features and fewer limitations than TenForker, so if MacOSXForker is available, it should generally be used.
However, MacOSXForker IS NOT the default on 10.0, even if it is available. Since MacOSXForker doesn't run on 10.0, the 10.0 platform is listed as a special case in MacPlatform's Implicator chain.
The MacOSXForker classes are provided in a separate jar-file. If you don't deploy this jar-file properly, the MacOSXForker classes won't work. You can merge this jar with other jar-files in your program, or leave it separate. If you leave it separate, you must include it in the program's classpath. If you don't do this, the MacOSXForker classes won't be found, even though the class-name is known to MacPlatform.
The MacOSXForker classes rely on a JNI-library file: "libMacOSXForkerIO.jnilib". This file contains the native code for the MacOSXForker classes. In general, this file should be placed in the same location as the jar that contains the MacOSXForker classes. Do not rename "libMacOSXForkerIO.jnilib". That's the name the MacOSXForker class knows the library as, and if you change it, then it won't be found.
If your program is a double-clickable jar, simply place "libMacOSXForkerIO.jnilib" in the same location as the double-clickable jar-file. It should then be found, because the default library-path contains ".".
Bundled double-clickable apps should place "libMacOSXForkerIO.jnilib" in the Contents/Resources/Java sub-directory within the app-bundle. That is, in the same place as the jar-file containing the MacOSXForker classes. This should work for Cocoa-Java apps, as well as "pure" Java apps.
JNLP (Java Web Start) applications should deploy the MacOSXForker classes in a jar, and the JNI-lib file using the normal JNLP native-library deployment mechanisms.
You can merge the MacOSXForker classes, or even all the MacBinary Toolkit classes, into a single jar-file for your app, if you wish. You can also maintain them as separate files, if you wish, with an appropriate classpath.
All FileForker imps have security implications for applets because they all refer to the fileystem. Even all-Java implementations like PlainForker need the appropriate security permission when run from an applet. Using a FileForker implementation in an applet also poses challenges for other reasons.First is the applet security and signing model. Only signed applets can escape the sandbox, and the signing and privilege model itself has evolved over the various Java releases. Exactly how to sign and privilege, and what to sign with, depends on the target platform. Even signed applets, though, cannot do certain things, such as supplying a native-code library which is subsequently loaded.
Second is the applet ClassLoader model, which is related to the security model. The security model is abstract, and is implemented by a ClassLoader in conjunction with the hosting application's SecurityManager and other classes. Since ClassLoader is an actual class, albeit an abstract one, it has specific relationships (e.g. inheritance) with other actual Java classes which the JVM enforces.
Third is the targeted host's deployment environment. An applet need not contain the MacBinary Toolkit classes if it can instead rely on the client machine already having them installed as a class library or Java extension. This might be done by a previously downloaded and executed installer application, specifically to "lay the foundation" for one or more applets that will be deployed more dynamically. An unsigned applet would still be unable to access the MacBinary Toolkit class library, but a signed applet with appropriate privileges should be able to use the installed classes. In short, the overall deployment model and the relationship between applets and class-libraries will affect MacBinary Toolkit usability on a given host platform.
The JDirect-based FileForker imps have the additional complication of referring to native code using Apple-specific JDirect technology. Depending on the applet's hosting application, it may or may not "play nicely" with JDirect. Common browsers, for example, may differ from all-Java hosting apps.
The JNI-based imps refer to native code contained in JNI-library files, which cannot usually be supplied by applets because it would violate the security model, which forbids untrusted and unverifiable native code supplied from remote origins.
- glguerin.io.imp.gen.PlainForker
glguerin.io.imp.gen.JavaOnlyForker
These classes should work in a signed applet. These imps are written entirely in plain ordinary Java, with no calls to native methods or platform-specific supporting classes. However, since neither imp actually does anything with resource-forks or metadata, the practical value of this is often quite limited.- glguerin.io.imp.gen.GenericForker
This imp calls MRJToolkit classes, or the stubs on non-Mac platforms. I don't know whether it will work in applets, signed or not, because I haven't tried it. Since it doesn't know about resource-forks, either, it's about as practical as the PlainForker imp, offering little advantage over that more portable imp.- glguerin.io.imp.mac.jd1.JD1Forker
glguerin.io.imp.mac.jd1.ResolvingJD1Forker
glguerin.io.imp.mac.jd2.JD2Forker
glguerin.io.imp.mac.jd2.ResolvingJD2Forker
glguerin.io.imp.mac.jd2.NineForker
glguerin.io.imp.mac.jd2.ResolvingNineForker
All these classical Mac OS imps use JDirect.JDirect calls made from applets, even signed applets, may or may not work when the hosting application is a browser. I think it may be browser-dependent. Or it could be that no browsers support it. I suspect this is due to the ClassLoader not knowing how to resolve the 'native' methods as JDirect methods, but I have no evidence or details on this, just a hunch.
JDirect does seem to work in applets hosted under the classical Applet Runner program. This is rarely of much practical value, but it seems worth mentioning.
- glguerin.io.imp.mac.ten.TenForker
glguerin.io.imp.mac.ten.ResolvingTenForker
These imps use JDirect-3, which is callable from a signed applet on Mac OS X, under version 10.0.* and 10.1.*. Sadly, 10.2.* has a problem with it, explained below.Because Mac OS X's Java is 1.3.* or higher, it uses the fine-grained permission model introduced in Java 1.2. Therefore, you will need file-system read and/or write permission in order for the MacBinary I/O classes to work.
There is a problem with JDirect calls in applets beginning with Mac OS 10.2. In that release of Mac OS X and Java, the previously allowed access to pivotal JDirect classes was restricted. As a result, even signed applets cannot refer to the Apple classes that provide the fundamental interface to JDirect. Worse, I can't tell you where this new restriction is defined. It isn't in the system-wide security-policy file, nor is it in a per-user policy file. However, I can tell you how these restrictions are expressed: as system properties with the naming pattern "package.restrict.access.com.apple...". These are boolean properties used by the AccessController to restrict packages. I don't know how or where these properties are set.
This new restriction prevents a signed applet from using the TenForker imps, even when it should be allowed under the verified security policies. This problem only appears on 10.2, when JDirect-based code is supplied in the signed applet. I don't think it applies to JDirect-based code provided in a Java extension, even though called from a signed applet. However, I have not investigated this.
The symptom of this package-restriction problem is that TenForker is unusable, even though appropriate permissions seem to be correct for the signed and trusted applet. Specifically, a SecurityException is thrown when trying to instantiate a TenForker. The SecurityException identifies a com.apple.mrj.* package as restricted, effectively making it entirely off-limits to the applet.
The only way to avoid this problem is to override the mistakenly restricted 10.2 policy with an expanded policy that grants access to the necessary JDirect packages. To do that, a per-user policy file in "~/.java.policy" must contain:
grant { permission java.lang.RuntimePermission "accessClassInPackage.com.apple.mrj.internal.jdirect"; permission java.lang.RuntimePermission "accessClassInPackage.com.apple.mrj.macos.frameworks"; };These permissions are granted to all codebases. This is not a security lapse, since the packages were previously available to all codebases under 10.1. I suspect the actual JDirect security is performed at the level of binding and calling native methods, not as a consequence of restricting package access.Unfortunately, this security policy must be in place on the client machine first. It cannot be installed and granted after the signed applet is loaded. This is a bit of a Catch-22, but once the policy is in effect, it need never be redone.
- glguerin.io.imp.mac.macosx.MacOSXForker
glguerin.io.imp.mac.macosx.CarbonMacOSXForker
These imps use JNI libraries for native-code support. Generally speaking, an applet cannot supply a JNI library-file that will be loaded into or onto the host machine. Therefore, I doubt whether these imps can be distributed in an applet, even a signed and all-privileged one.I think that if the MacBinary Toolkit jars and JNI-lib files are already installed on the client machine in a Java extensions directory, then they will be available to an appropriately signed and privileged applet. However, I have not tried this, since I don't write applets.
A JNLP (Java Web Start) application can distribute JNI libs with its download. But it's not an applet.
To Greg's Home Page
To Greg's Software Page