Attached is a quickie implementation of a class loader, if it will help. I didn't touch resource URLs, but an approach I've used with a lot of success in the past is to use a form like

jar:/path/to/jarfile?file/within/jarfile

That's easy to construct, easy to parse, unique, and not easily confused with standard URLs.

Something occured to me a few minutes ago - conceptually the entire retrieval subsection is another classloader. In fact, it should be a SecureClassLoader - the SecurityManager could be use to restrict access to some upstream sites (e.g., you could disable access to a site you believe to have been compromised), local write access to the repository, etc.

In this case the classloader I mentioned above would disappear. Somewhere near the top of the program the new classloader would be specified, then everyone would just load files as usual. The classloader (and security manager) would decide whether to download files, where to cache them, etc. Everyone else would either see requests succeed or a ClassNotFoundException.

Brett Porter wrote:
Already fixed in CVS for b10 - the whole maven installation can now be read only and the plugins are expanded to ~/.maven/plugins

I guess I'm still confused why they're expanded at all. Writing files is hard - how good is Java at guarding against symlink attacks, race conditions, etc.?


The second issue is more subtle.  When packages are downloaded,
they are put into the global $MAVEN_HOME/repository.

Define maven.repo.local in ~/build.properties and it will store it wherever you choose. In b10, the default is ~/.maven/repository

What if you want that split behavior? Or prohibiting downloading any file by an untrusted user - they need to ask a trusted user to retrieve upstream packages for them.


I guess I should cut to the chase - where's the formal security policy? That would identify roles, rights, etc.

I've found current CVS to be at least as stable as b9 if not more so, and its easy to build with the bootstrap, so I'd recommend giving it a go.

I've been unable to get any snapshot to compile yet. I didn't try to compile the current CVS head because I'm trying to rebuild and package other Apache packages and need to provide a provenance of all tools - whatever I use has to be recreatable by others, so a tagged CVS snapshot is okay, the "current contents" are not.


Running the binary package was a stopgap measure - self-compiling a tool from its own binary is very risky (see the CACM article for the gruesome details why), but it would allow me to proceed provisionally.

Don't panic ;)

I wasn't panicked. Pissed at having wasted hours, but not panicked. :-)
/**
 * Quickie classloader that loads jar files in "plugins" directory.
 * It is not guaranteed to work - I've done SQL-based classloaders
 * in the past, but it's been awhile.
 *
 * A related "SecureClassLoader" class should be defined - one that
 * explicitly supports a security manager for plugins.  (E.g., it
 * would allow someone to limit access to embedded control files, etc.)
 *
 * This implementation cases the contents of the directory in memory.
 * With a bit more work the mapping from "name" -> jarfile could be
 * cached, with the item read only when needed for the first time.
 * (The parent ClassLoader class should handle caching.)
 *
 * Released to "Maven" group for use as they see fit.
 */
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;

public class MavenClassLoader extends ClassLoader {

	// wrapper for byte array in 'java.util.map' object.
	class CLObject {
		byte[] b;
		int len;

		CLObject (InputStream is, ZipEntry ze) throws IOException {
			len = (int) ze.getSize();
			int n;
			b = new byte[len];

			for (int off = 0; off < len; off += n) {
				n = is.read(b, off, len-off);
			}
		}

		int length() {
			return len;
		}

		byte[] getBytes() {
			return b;
		}
	};

//	protected Map classes = new HashMap();
//	protected Map properties = new HashMap();

	protected Map classes = new TreeMap(); // for debugging...
	protected Map properties = new TreeMap(); // for debugging...

	//
	// Constructor: reads all jar files in a directory and caches
	// the contents for later retrieval.
	//
	public MavenClassLoader (String pluginDir) throws IOException {

		super();

		String[] files = new File(pluginDir).list();

		for (int i = 0; i < files.length; i++) {
			if (!files[i].endsWith(".jar")) {
				continue;
			}

			String filename = pluginDir + File.separator + files[i];
			cacheJar(new JarFile(filename));
		}
	}

	//
	// Recursively cache contents of jar file.
	//
	private void cacheJar (JarFile jf) throws IOException {

		ZipEntry ze;
		Enumeration e = jf.entries();

		while (e.hasMoreElements()) {
			ze = (ZipEntry) e.nextElement();
			if (ze.isDirectory()) {
				continue;
			}

			String name = ze.getName();
			if (name.startsWith("META-INF/")) {
				continue;
			}

			try {
				// class file?
				// use "name.replace('/','.')" ?
				if (name.endsWith(".class")) {
					classes.put(name, new CLObject(jf.getInputStream(ze), ze));
				}

				// recursively process embedded jar files
				else if (name.endsWith(".jar")) {
					cacheJar(jf);
				}

				// certain items with common names...
				else if (name.equals("project.properties") ||
					name.equals("project.xml") ||
					name.equals("plugin.properties") ||
					name.equals("plugin.jelly")) {

					String jname = jf.getName();
					int idx = jname.lastIndexOf("-plugin");
					if (idx > 1) {
						jname = jname.substring(0, idx);
					}
					jname = jname + "." + name;

					properties.put(jname,
						new CLObject(jf.getInputStream(ze), ze));
				}

				// anything else....
				else {
					properties.put(name,
						new CLObject(jf.getInputStream(ze), ze));
				}
			}
			catch (IOException ioe) {
				System.err.println(ioe.toString());
			}
		}
	}

	private void cacheJar (JarInputStream jis) throws IOException {
		throw new RuntimeException("not implemented yet!");
	}

	public Class findClass (String name) throws ClassNotFoundException {
		byte[] b = loadClassData(name);
		return defineClass(name, b, 0, b.length);
	}

	private byte[] loadClassData (String name) throws ClassNotFoundException {
		if (!classes.containsKey(name)) {
			throw new ClassNotFoundException("name");
		}
		CLObject clo = (CLObject) classes.get(name);
		return clo.getBytes();
	}

	public InputStream getResourceAsStream (String name) {
		if (!properties.containsKey(name)) {
			return null;
		}
	
		CLObject clo = (CLObject) properties.get(name);
		return new ByteArrayInputStream(clo.getBytes());
	}

	static public final void main (String[] args) throws Exception {

		MavenClassLoader cl = new MavenClassLoader("./plugins");

		Iterator i = cl.classes.keySet().iterator();
		while (i.hasNext()) {
			System.out.println((String) i.next());
		}
		System.out.println("");

		i = cl.properties.keySet().iterator();
		while (i.hasNext()) {
			System.out.println((String) i.next());
		}
		System.out.println("");

		byte[] b = new byte[512];
		int n;
		InputStream is = cl.getResourceAsStream("plugin-resources/ui.properties");
		while ((n = is.read(b)) > 0) {
			System.out.write(b, 0, n);
		}
		is.close();
	}
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to