/*
*
* 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.
*/
/*
* @(#)MIDPConfig.java 1.11 06/10/30
* This class contains all the information necessary
* to configure a MemberFilter appropriate for MIDP2.0
* as well as some tables we need to configure the
* MIDPImplementationClassLoader
*/
package sun.misc;
import java.net.URL;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.io.IOException;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.Vector;
import java.net.MalformedURLException;
import java.util.HashSet;
public final
class MIDPConfig{
/* The MIDP library classloader */
private static MIDPImplementationClassLoader midpImplCL;
/* The midlet classloader */
/*private static MIDletClassLoader midletCL;*/
/* The MemberFilter */
private static MemberFilter memberFilter;
/* The classes allowed for Midlet */
private static HashSet allowedClasses;
public static String MIDPVersion = "2.0";
public static String CLDCVersion = "1.1";
/*
* The following data structures are for
* managing name visibility.
*/
static String
systemPackages[] = {
"java.",
"javax.microedition."
};
static {
// Create the member filter.
memberFilter = newMemberFilter();
}
private static File[] getDefaultPath() throws IOException {
String libdir = System.getProperty("java.home") +
File.separator + "lib" + File.separator;
String jars[] = split(System.getProperty(
"com.sun.midp.implementation"), ' ');
int num = jars.length;
File files[] = new File[num];
for (int i = 0; i<num; i++) {
String jar = libdir + jars[i];
File f = new File(jar);
if (!f.exists()) {
throw new IOException("Can't find " + jar);
}
files[i] = f;
}
return files;
}
/*
* Set up a MemberFilter using the classes and members
* given in the permittedMembers structures above.
* All MIDletClassLoaders will share the same MemberFilter
* since using it does not change its state.
*/
public static MemberFilter
newMemberFilter(){
try{
String filename =
System.getProperty("java.home") + File.separator +
"lib" + File.separator + "MIDPFilterConfig.txt";
MemberFilterConfig mfc = new MemberFilterConfig(filename);
MemberFilter mf;
// DEBUG System.out.println(
// "Starting MemberFilter file parsing");
// DEBUG mfc.setVerbose(true);
mf = mfc.parseFile();
// DEBUG System.out.println("Done MemberFilter file parsing");
return mf;
}catch(java.io.IOException e){
e.printStackTrace();
return null;
}
}
private static void
readPermittedClassFile(BufferedReader infile) {
try{
while(true){
String inline = infile.readLine();
if (inline == null)
break; // eof
if (inline.length() == 0)
continue; // blank line
if (inline.charAt(0) == '#')
continue; // comment
allowedClasses.add(inline.intern());
}
}catch(java.io.IOException e){
}
}
/* Read the permitted class list. */
private static void
getPermittedClasses() {
BufferedReader infile;
allowedClasses = new HashSet();
/* First, read the default lib/MIDPPermittedClasses.txt */
try {
String filename =
System.getProperty("java.home") + File.separator +
"lib" + File.separator + "MIDPPermittedClasses.txt";
infile = new BufferedReader(new FileReader(filename));
readPermittedClassFile(infile);
infile.close();
} catch (IOException ie) {
throw new InternalError(
"Failed to read lib/MIDPPermittedClasses.txt");
}
/* Then, search the jar files in the bootclasspath to see if
* there are additional class lists. */
String jarfiles[] = split(
System.getProperty("sun.boot.class.path"),
File.pathSeparatorChar);
int num = jarfiles.length;
for (int i = 0; i < num; i++) {
try {
JarFile jarfile = new JarFile(jarfiles[i]);
ZipEntry entry = jarfile.getEntry("MIDPPermittedClasses.txt");
if (entry != null) {
InputStream jis = jarfile.getInputStream(entry);
infile = new BufferedReader(new InputStreamReader(jis));
readPermittedClassFile(infile);
infile.close();
}
} catch (IOException ioe) {}
}
}
public static MIDPImplementationClassLoader
getMIDPImplementationClassLoader() {
return midpImplCL;
}
public static MIDPImplementationClassLoader
newMIDPImplementationClassLoader(File files[]){
/* The MIDPImplementationClassLoader already exist. Throw an
* exception.
*/
if (midpImplCL != null) {
throw new InternalError(
"The MIDPImplementationClassLoader is already created");
}
PermissionCollection perms = new Permissions();
if (files == null || files.length == 0) {
try {
files = getDefaultPath();
} catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
return null;
}
}
URL urls[] = new URL[files.length];
for (int i = 0; i < files.length; i++) {
try {
urls[i] = files[i].toURL();
} catch (MalformedURLException e) {
e.printStackTrace();
urls = null;
break;
}
}
perms.add(new java.security.AllPermission());
//DEBUG System.out.println(
// "Constructing MIDPImplementationClassLoader with permissions "
// +perms);
midpImplCL = new MIDPImplementationClassLoader(
urls, perms, null);
return midpImplCL;
}
/*
* Set up the permissions that will be granted to MIDlet code proper.
* Currently this is very little: only the ability to modify the properties
* of a Thread. And this is very limited by API hiding so is not very dangerous.
* We absolutely do not give them vmExit, which is explicitly prohibited.
* This set of permissions is read-only and shared by all MIDletClassLoaders.
*
* Property access cannot be dealt with using Java permissions, as we
* want to make properties disappear, and permissions will throw an Exception
* to prohibit seeing the property.
*
* This depends on being run in the following environment:
* security enabled, but all permissions granted to main program.
* This is achieved but putting -Djava.security.manager on
* the command line, and having my own .java.policy file
* that looks like this:
* grant codeBase "file:*" {
* permission java.security.AllPermission;
* };
*/
static PermissionCollection
newMidletPermissions(){
PermissionCollection mp = new Permissions();
mp.add(new java.lang.RuntimePermission("modifyThread"));
mp.add(new java.util.PropertyPermission("*", "read"));
mp.setReadOnly();
return mp;
}
static PermissionCollection midletPermissions = newMidletPermissions();
/*
* Set up a new MIDletClassLoader
* There should probably be one of these per MIDlet suite.
* This would allow sharing between suite members, including data.
*/
static String[]
split(String path, char separator){
int nComponents = 1;
//char separator = ' ';
String components[];
int length;
int start;
int componentIndex;
if (path == null) {
components = new String[0];
return components;
}
length = path.length();
for (int i=0; i<length; i++){
if (path.charAt(i) == separator)
nComponents += 1;
}
components = new String[nComponents];
start = 0;
componentIndex = 0;
/* could optimize here for the common case of nComponents == 1 */
for (int i=0; i<length; i++){
if (path.charAt(i) == separator){
components[componentIndex] = path.substring(start, i);
componentIndex += 1;
start = i+1;
}
}
/* and the last components is delimited by end of String */
components[componentIndex] = path.substring(start, length);
return components;
}
/*
* This version allows the caller to specify a set of permissions.
* This is less useful than the usual version, which grants the
* permissions we plan on granting to MIDlets.
*
* The 'enableFilter' argument specifies that if the API hiding
* filter is being enabled. If the filter is enabled, midlet
* can only access CLDC/MIDP classes. If the filter is disabled,
* midlet can access all classes on the bootclasspath, including
* all the CDC classes.
*
* The 'auxClassLoader' is a helper classloader used when the
* MIDletClassLoader and its parents fail to load the requested
* class.
*/
private static MIDletClassLoader
newMIDletClassLoader(
String midpPath[], MemberFilter mf,
PermissionCollection perms,
MIDPImplementationClassLoader implClassLdr,
boolean enableFilter,
ClassLoader auxClassLoader,
MIDPBridgeInterface bridgeInterface)
{
if (midpImplCL == null) {
throw new InternalError(
"Need to create the parent MIDPImplementationClassLoader first");
}
URL midJarURL[];
int nComponents = midpPath.length;
midJarURL = new URL[nComponents];
try {
for (int i=0; i<nComponents; i++){
midJarURL[i] = new File(midpPath[i]).toURI().toURL();
}
}catch(Exception e){
System.err.println("URL Creation:");
e.printStackTrace();
return null;
}
//DEBUG System.out.println(
// "Constructing MIDletClassLoader with permissions "+perms);
MIDletClassLoader midletCL = new MIDletClassLoader(
midJarURL, systemPackages,
perms, mf, implClassLdr,
enableFilter, auxClassLoader,
bridgeInterface);
return midletCL;
}
/*
* This version allows the caller to specify a set of permissions.
* The parent classloader is the MIDPImplementationClassLoader.
* The API hiding filter is enabled.
*/
public static MIDletClassLoader
newMIDletClassLoader(
String midpPath[], PermissionCollection perms)
{
return newMIDletClassLoader(midpPath,
memberFilter,
perms,
midpImplCL,
true,
null,
null);
}
/*
* Use the default midlet permission collection. The parent classloader
* is the MIDPImplementationClassLoader. The API hiding filter is
* enabled.
*/
public static MIDletClassLoader
newMIDletClassLoader(String midpPath[])
{
return newMIDletClassLoader(midpPath,
memberFilter,
midletPermissions,
midpImplCL,
true,
null,
null);
}
/*
* The 'enableFilter' argument specifies that if the API hiding
* filter is being enabled. If the filter is enabled, midlet
* can only access CLDC/MIDP classes. If the filter is disabled,
* midlet can access all classes on the bootclasspath, including
* all the CDC classes.
*
* The 'auxClassLoader' is a helper classloader used when the
* MIDletClassLoader and its parents fail to load the requested
* class.
*/
public static MIDletClassLoader
newMIDletClassLoader(String midpPath[], boolean enableFilter,
ClassLoader auxClassLoader)
{
return newMIDletClassLoader(midpPath,
memberFilter,
midletPermissions,
midpImplCL,
enableFilter,
auxClassLoader,
null);
}
public static MIDletClassLoader
newMIDletClassLoader(String midpPath[], boolean enableFilter,
PermissionCollection perms,
MIDPBridgeInterface bridgeInterface)
{
return newMIDletClassLoader(midpPath,
memberFilter,
perms,
midpImplCL,
enableFilter,
null,
bridgeInterface);
}
/*
* Get the MIDletClassLoader instance that loads the caller
* midlet class.
*/
public static MIDletClassLoader
getMIDletClassLoader() {
int i = 1; /* skip the direct caller, who must be system code */
ClassLoader loader = null;
Class cl = CVM.getCallerClass(i);
while (cl != null) {
loader = cl.getClassLoader();
if (loader instanceof MIDletClassLoader) {
return (MIDletClassLoader)loader;
}
cl = CVM.getCallerClass(++i);
}
return null;
}
/*
* Call getResourceAsStream on the first MIDlet class loader on the stack.
*/
public static InputStream getMIDletResourceAsStream(String name) {
ClassLoader cl = getMIDletClassLoader();
if (cl == null) {
return null;
}
return cl.getResourceAsStream(name);
}
/*
* A utility method used to load a resource using all of the caller's
* classloaders. This is useful when application and system
* code are loaded by different classloaders. Note this will
* throw a security exception if the MIDlet does not have permission
* to get resources other than its own, unless called by system code
* within a doPriviledged method.
*/
public static InputStream getSystemResourceAsStream(String name) {
int i = 1; /* skip the caller, which must be system code */
InputStream is = null;
ClassLoader lastFailedLoader = null;
/* This is a bit slow since we need to walk up the stack.
* Because we don't know which classloader to load the resource,
* so we have to do it the hard way. */
while (is == null) {
Class cl = sun.misc.CVM.getCallerClass(i);
if (cl == null) { /* reach the top of the stack */
break;
}
ClassLoader loader = cl.getClassLoader();
if (i == 1 || loader != lastFailedLoader) {
is = cl.getResourceAsStream(name);
if (is != null) {
break;
} else {
lastFailedLoader = loader;
}
}
i++; /* the next caller */
}
return is;
}
/* Check if the class is allowed for midlet. */
public static boolean isClassAllowed(String classname) {
// Get the permitted class list.
if (allowedClasses == null) {
getPermittedClasses();
}
return allowedClasses.contains(classname);
}
}