/* * @(#)MIDletClassLoader.java 1.11 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ /* * @(#)MIDletClassLoader.java 1.5 03/07/09 * * Class loader for midlets running on CDC/PP * * It loads from a single JAR file and deligates to other class loaders. * It has a few of interesting properties: * 1) it is careful, perhaps overly so, about not loading classes that * are in system packages from the JAR file persumed to contain the MIDlet * code. * 2) it uses a MemberFilter to process classes for illegal field references. * This is easiest to do after the constant pool is set up. * 3) it remembers which classes have failed the above test and refuses to * load them, even though the system thinks they're already loaded. * * It lets the underlying URLClassLoader do all the hard work. * */ package sun.misc; import java.net.URL; import java.net.URLConnection; import java.net.URLClassLoader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.AccessController; import java.security.AccessControlContext; import java.security.PrivilegedAction; public class MIDletClassLoader extends URLClassLoader { URL myBase[]; String[]systemPkgs; private MemberFilter memberChecker; /* to check for amputated members */ private PermissionCollection perms; private HashSet badMidletClassnames = new HashSet(); private MIDPImplementationClassLoader implementationClassLoader; private AccessControlContext ac = AccessController.getContext(); /* * If the filter is disabled, all classes on the bootclasspath * can be accessed from midlet. */ private boolean enableFilter; private ClassLoader auxClassLoader; private MIDPBridgeInterface bridgeInterface; public MIDletClassLoader( URL base[], String systemPkgs[], PermissionCollection pc, MemberFilter mf, MIDPImplementationClassLoader parent, boolean enableFilter, ClassLoader auxClassLoader, MIDPBridgeInterface bridgeInterface) { super(base, parent); myBase = base; this.systemPkgs = systemPkgs; memberChecker = mf; perms = pc; implementationClassLoader = parent; this.enableFilter = enableFilter; this.auxClassLoader = auxClassLoader; this.bridgeInterface = bridgeInterface; /* Register the classloader */ String p = base[0].getPath(); int idx = p.lastIndexOf('.'); if (idx != -1) { String name = p.substring( p.lastIndexOf(File.separatorChar)+1, idx); CVM.Preloader.registerClassLoader(name, this); } } protected PermissionCollection getPermissions(CodeSource cs){ URL srcLocation = cs.getLocation(); for (int i=0; i<myBase.length; i++){ if (srcLocation.equals(myBase[i])){ return perms; } } return super.getPermissions(cs); } /* Check if class belongs to restricted system packages. */ private boolean packageCheck(String pkg) { String forbidden[] = systemPkgs; int fLength = forbidden.length; /* First check the default list specified by MIDPConfig */ for (int i=0; i< fLength; i++){ if (pkg.startsWith(forbidden[i])){ return true; } } /* Then Check with MIDPPkgChecker. The MIDPPkgChecker knows * the restricted MIDP and JSR packages specified in their * rom.conf files. */ return MIDPPkgChecker.checkPackage(pkg); } private Class loadFromUrl(String classname) throws ClassNotFoundException { Class newClass; try { newClass = super.findClass(classname); // call URLClassLoader }catch(Exception e){ /*DEBUG e.printStackTrace(); */ // didn't find it. return null; } if (newClass == null ) return null; /* * Found the requested class. Make sure it's not from * restricted system packages. */ int idx = classname.lastIndexOf('.'); if (idx != -1) { String pkg = classname.substring(0, idx); if (packageCheck(pkg)) { throw new ClassNotFoundException(classname + ". Prohibited package name: " + pkg); } } /* * Check member access to make sure the class doesn't * access any hidden CDC APIs. */ try { // memberChecker will throw an Error if it doesn't like // the class. if (enableFilter) { memberChecker.checkMemberAccessValidity(newClass); } return newClass; } catch (Error e){ // If this happens, act as if we cannot find the class. // remember this class, too. If the MIDlet catches the // Exception and tries again, we don't want findLoadedClass() // to return it!! badMidletClassnames.add(classname); throw new ClassNotFoundException(e.getMessage()); } } public synchronized Class loadClass(String classname, boolean resolve) throws ClassNotFoundException { Class resultClass; Throwable err = null; int i = classname.lastIndexOf('.'); if (i != -1) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPackageAccess(classname.substring(0, i)); } } classname = classname.intern(); if (badMidletClassnames.contains(classname)){ // the system thinks it successfully loaded this class. // But the member checker does not think we should be able // to use it. We threw an Exception before. Do it again. throw new ClassNotFoundException(classname.concat( " contains illegal member reference")); } /* First, check to see if the class has already been loaded. */ resultClass = findLoadedClass(classname); /* Class has not been loaded. Delegate to the parent classloader. */ if (resultClass == null){ try { /* Ask the parent to load the class for us. But first * make sure the class is allowed to be accessed by * midlet. */ if (!enableFilter || MIDPConfig.isClassAllowed(classname)) { resultClass = implementationClassLoader.loadClass( classname, false); } } catch (ClassNotFoundException e) { } catch (NoClassDefFoundError e) { } } /* Try loading the class ourselves. */ if (resultClass == null) { try { resultClass = loadFromUrl(classname); } catch (ClassNotFoundException e) { err = e; } catch (NoClassDefFoundError e) { err = e; } } /* * If MIDletClassLoader and the parents failed to * load the class, try the auxClassLoader. */ if (resultClass == null && auxClassLoader != null) { resultClass = auxClassLoader.loadClass(classname); } if (resultClass == null && bridgeInterface != null) { resultClass = bridgeInterface.loadClass(classname); } if (resultClass == null) { if (err == null) { throw new ClassNotFoundException(classname); } else { if (err instanceof ClassNotFoundException) { throw (ClassNotFoundException)err; } else { throw (NoClassDefFoundError)err; } } } if (resolve) { resolveClass(resultClass); } return resultClass; } public InputStream getResourceAsStream(String name){ // prohibit reading .class as a resource if (name.endsWith(".class")){ return null; // not allowed! } int i; // Replace /./ with / while ((i = name.indexOf("/./")) >= 0) { name = name.substring(0, i) + name.substring(i + 2); } // Replace /segment/../ with / i = 0; int limit; while ((i = name.indexOf("/../", i)) > 0) { if ((limit = name.lastIndexOf('/', i - 1)) >= 0) { name = name.substring(0, limit) + name.substring(i + 3); i = 0; } else { i = i + 3; } } // The JAR reader cannot find the resource if the name starts with // a slash. So we remove the leading slash if one exists. if (name.startsWith("/") || name.startsWith(File.separator)) { name = name.substring(1); } final String n = name; // do not delegate. We only use our own URLClassLoader.findResource to // look in our own JAR file. That is always allowed. // Nothing else is. InputStream retval; retval = (InputStream) AccessController.doPrivileged( new PrivilegedAction(){ public Object run(){ URL url = findResource(n); try { return url != null ? url.openStream() : null; } catch (IOException e) { return null; } } }, ac); return retval; } }